Skip to content

Commit 9f01f09

Browse files
author
Philipp Molitor
authored
Merge pull request #8 from PhilippMolitor/dev
release 2020.0.1
2 parents afeb4d5 + c1d3112 commit 9f01f09

19 files changed

+863
-113
lines changed

.eslintrc.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ module.exports = {
2626
tsconfigRootDir: __dirname,
2727
project: ['./tsconfig.json'],
2828
},
29-
ignorePatterns: ['dist/**'],
29+
ignorePatterns: [
30+
'dist/**',
31+
'src/**/*.test.ts',
32+
'src/**/*.test.tsx',
33+
'jest.*.js',
34+
],
3035
globals: {
3136
React: true,
3237
JSX: true,

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# node
22
node_modules/
33
dist/
4+
coverage/
45

56
# npm/yarn
67
yarn*.log

.npmignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
# node
55
node_modules/
66
src/
7+
coverage/
78
.husky/
89
.prettierrc
910
.eslintrc.js
1011
.eslintcache
1112
tsconfig.json
1213

1314
# jest
15+
jest.*.js
16+
jest.*.ts
1417
__tests__/*
1518
*.test.ts
1619
*.test.tsx

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<img src="https://img.shields.io/npm/dw/react-unity-renderer?style=flat-square">
77
<img src="https://img.shields.io/github/stars/PhilippMolitor/react-unity-renderer?style=flat-square">
88
<img src="https://img.shields.io/npm/v/react-unity-renderer?style=flat-square">
9+
<img src="https://img.shields.io/bundlephobia/minzip/react-unity-renderer?style=flat-square">
910

1011
</p>
1112

jest.config.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'jsdom',
4+
roots: ['<rootDir>/src'],
5+
collectCoverage: true,
6+
coverageDirectory: 'coverage',
7+
transform: {
8+
'^.+\\.tsx?$': 'ts-jest',
9+
},
10+
setupFilesAfterEnv: [
11+
'@testing-library/jest-dom/extend-expect',
12+
'<rootDir>/jest.setup.js',
13+
],
14+
};

jest.setup.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const enzyme = require('enzyme');
2+
const Adapter = require('enzyme-adapter-react-16');
3+
4+
enzyme.configure({ adapter: new Adapter() });

package.json

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-unity-renderer",
3-
"version": "2020.0.0",
3+
"version": "2020.0.1",
44
"description": "React Unity Renderer allows to interactively embed Unity WebGL builds into a React powered project.",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",
@@ -17,8 +17,11 @@
1717
],
1818
"scripts": {
1919
"watch": "tsc --build ./tsconfig.json --watch",
20-
"build": "rimraf dist && tsc --build ./tsconfig.json",
21-
"test": "echo \"no tests defined yet\" && exit 1",
20+
"build": "yarn clean && tsc --build ./tsconfig.json",
21+
"build:dev": "yarn build && yarn pack",
22+
"clean": "rimraf dist && rimraf *.tgz",
23+
"test": "jest",
24+
"test:watch": "jest --watchAll",
2225
"lint": "eslint --fix ./src/",
2326
"lint:staged": "lint-staged",
2427
"prepare": "yarn build"
@@ -29,12 +32,23 @@
2932
],
3033
"{src,typings}/**/*.{js,jsx,ts,tsx}": [
3134
"eslint --fix"
35+
],
36+
"src/**/*.{ts,tsx}": [
37+
"jest --bail --findRelatedTests"
3238
]
3339
},
3440
"devDependencies": {
41+
"@testing-library/jest-dom": "^5.11.9",
42+
"@testing-library/react": "^11.2.5",
43+
"@types/enzyme": "^3.10.8",
44+
"@types/enzyme-adapter-react-16": "^1.0.6",
45+
"@types/jest": "^26.0.20",
3546
"@types/react": "^16.8.0",
3647
"@types/react-dom": "^16.8.0",
3748
"@typescript-eslint/eslint-plugin": "^4.15.2",
49+
"@typescript-eslint/parser": "^4.16.1",
50+
"enzyme": "^3.11.0",
51+
"enzyme-adapter-react-16": "^1.15.6",
3852
"eslint": "^7.20.0",
3953
"eslint-config-airbnb-typescript": "^12.0.0",
4054
"eslint-config-prettier": "^7.2.0",
@@ -45,9 +59,13 @@
4559
"eslint-plugin-react": "^7.21.5",
4660
"eslint-plugin-react-hooks": "^4.2.0",
4761
"husky": "^5.1.1",
62+
"jest": "^26.6.3",
4863
"lint-staged": "^10.5.4",
4964
"prettier": "^2.2.1",
65+
"react": "^16.8.0",
66+
"react-dom": "^16.8.0",
5067
"rimraf": "^3.0.2",
68+
"ts-jest": "^26.5.2",
5169
"typescript": "^4.*"
5270
},
5371
"peerDependencies": {

src/components/UnityRenderer.ts

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import '../global';
2-
31
import { createElement, HTMLAttributes, useEffect, useState, VFC } from 'react';
42

53
import { UnityContext } from '..';
64

7-
import { UnityLoaderService } from '../loader';
5+
import { UnityLoaderService } from '../lib/loader';
86

97
export type UnityRendererProps = Omit<
108
HTMLAttributes<HTMLCanvasElement>,
@@ -65,39 +63,11 @@ export const UnityRenderer: VFC<UnityRendererProps> = ({
6563
}
6664

6765
/**
68-
* Uses the native Unity loader method to attach the Unity instance to
69-
* the renderer `canvas`.
70-
*
71-
* @returns {Promise<void>} A Promise resolving on successful mount of the
72-
* Unity instance.
73-
*/
74-
async function mount(): Promise<void> {
75-
// get the current loader configuration from the UnityContext
76-
const c = ctx.getConfig();
77-
78-
// attach Unity's native JavaScript loader
79-
await loader!.execute(c.loaderUrl);
80-
81-
const instance = await window.createUnityInstance(
82-
renderer!,
83-
{
84-
dataUrl: c.dataUrl,
85-
frameworkUrl: c.frameworkUrl,
86-
codeUrl: c.codeUrl,
87-
streamingAssetsUrl: c.streamingAssetsUrl,
88-
companyName: c.companyName,
89-
productName: c.productName,
90-
productVersion: c.productVersion,
91-
},
92-
(p) => onUnityProgress(p)
93-
);
94-
95-
// set the instance for further JavaScript <--> Unity communication
96-
ctx.setInstance(instance);
97-
}
98-
99-
/**
66+
* Unmounts the game by shutting its instance down, removing the loader
67+
* script from the DOM and sending the appropriate events via the props.
10068
*
69+
* @param {() => void} onComplete Callback function which will be executed
70+
* after the unmounting has completed.
10171
*/
10272
function unmount(onComplete?: () => void) {
10373
ctx.shutdown(() => {
@@ -109,10 +79,49 @@ export const UnityRenderer: VFC<UnityRendererProps> = ({
10979
if (onUnityReadyStateChange) onUnityReadyStateChange(false);
11080
setLastReadyState(false);
11181

82+
// callbck
11283
if (onComplete) onComplete();
11384
});
11485
}
11586

87+
/**
88+
* Uses the native Unity loader method to attach the Unity instance to
89+
* the renderer `canvas`.
90+
*
91+
* @returns {Promise<void>} A Promise resolving on successful mount of the
92+
* Unity instance.
93+
*/
94+
async function mount(): Promise<void> {
95+
try {
96+
// get the current loader configuration from the UnityContext
97+
const c = ctx.getConfig();
98+
99+
// attach Unity's native JavaScript loader
100+
await loader!.execute(c.loaderUrl);
101+
102+
const instance = await window.createUnityInstance(
103+
renderer!,
104+
{
105+
dataUrl: c.dataUrl,
106+
frameworkUrl: c.frameworkUrl,
107+
codeUrl: c.codeUrl,
108+
streamingAssetsUrl: c.streamingAssetsUrl,
109+
companyName: c.companyName,
110+
productName: c.productName,
111+
productVersion: c.productVersion,
112+
},
113+
(p) => onUnityProgress(p)
114+
);
115+
116+
// set the instance for further JavaScript <--> Unity communication
117+
ctx.setInstance(instance);
118+
} catch (e) {
119+
unmount(() => {
120+
if (onUnityError) onUnityError(e);
121+
});
122+
}
123+
}
124+
116125
// on loader + renderer ready
117126
useEffect(() => {
118127
if (!loader || !renderer) return;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { mount, ReactWrapper } from 'enzyme';
2+
3+
import { UnityContext } from '../../lib/context';
4+
import { UnityRenderer } from '../UnityRenderer';
5+
6+
describe('<UnityRenderer /> (unconfigured)', () => {
7+
const loaderUrl = 'http://example.com/script.js';
8+
9+
const ctx = new UnityContext({
10+
loaderUrl: loaderUrl,
11+
codeUrl: '',
12+
dataUrl: '',
13+
frameworkUrl: '',
14+
});
15+
16+
let renderer: ReactWrapper<typeof UnityRenderer>;
17+
let progress = 0;
18+
let ready = false;
19+
let error = false;
20+
21+
beforeEach(() => {
22+
renderer = mount<typeof UnityRenderer>(
23+
<UnityRenderer
24+
context={ctx}
25+
onUnityProgressChange={(p) => (progress = p)}
26+
onUnityReadyStateChange={(r) => (ready = r)}
27+
onUnityError={() => (error = true)}
28+
className="test"
29+
/>
30+
);
31+
});
32+
33+
it('renders with minimal configuration', async () => {
34+
expect(renderer).toBeDefined();
35+
});
36+
37+
it('uses the context prop', async () => {
38+
expect(renderer.prop('context')).toBe(ctx);
39+
});
40+
41+
it('uses the className prop', async () => {
42+
expect(renderer.prop('className')).toBe('test');
43+
});
44+
45+
it('does not trigger callbacks yet', async () => {
46+
expect(progress).toBe(0);
47+
expect(ready).toBe(false);
48+
expect(error).toBe(false);
49+
});
50+
});

src/global.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
/* eslint-disable import/no-cycle */
2-
export { UnityContext, UnityEventCallback } from './context';
2+
export {
3+
UnityContext,
4+
UnityLoaderConfig,
5+
UnityInstanceConfig,
6+
} from './lib/context';
37
export { UnityRenderer, UnityRendererProps } from './components/UnityRenderer';
4-
export { UnityLoaderConfig } from './interfaces/config';

src/interfaces/config.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/lib/__tests__/context.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { UnityContext, UnityLoaderConfig } from '../context';
2+
3+
describe('UnityContext', () => {
4+
const cfg: UnityLoaderConfig = {
5+
loaderUrl: 'a',
6+
codeUrl: 'b',
7+
frameworkUrl: 'c',
8+
dataUrl: 'd',
9+
streamingAssetsUrl: 'e',
10+
symbolsUrl: 'f',
11+
memoryUrl: 'g',
12+
modules: { h: true },
13+
companyName: 'i',
14+
productName: 'j',
15+
productVersion: 'k',
16+
};
17+
18+
it('stores and retrieves the loader configuration', async () => {
19+
const ctx = new UnityContext(cfg);
20+
21+
expect(ctx.getConfig()).toEqual(cfg);
22+
});
23+
});

src/lib/__tests__/loader.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { fireEvent } from '@testing-library/dom';
2+
import { UnityLoaderService } from '../loader';
3+
4+
describe('UnityLoaderService', () => {
5+
const url = 'http://example.com/script.js';
6+
let loader: UnityLoaderService;
7+
8+
beforeEach(() => {
9+
loader = new UnityLoaderService();
10+
});
11+
12+
it('can create a loader', async () => {
13+
expect(loader).toBeInstanceOf(UnityLoaderService);
14+
});
15+
16+
it('resolves on <script> load', async (done) => {
17+
loader.execute(url).then(() => {
18+
expect(script!.src).toEqual(url);
19+
done();
20+
});
21+
22+
const script = document.querySelector('script');
23+
24+
expect(script).toBeDefined();
25+
fireEvent.load(script!);
26+
});
27+
28+
it('can unmount itself', async () => {
29+
loader.execute(url).then(() => {
30+
loader.unmount();
31+
expect(script).not.toBeDefined();
32+
});
33+
34+
const script = document.querySelector('script');
35+
36+
expect(script).toBeDefined();
37+
fireEvent.load(script!);
38+
});
39+
});

0 commit comments

Comments
 (0)