Skip to content

Commit

Permalink
chore: add SSR tests (test command)
Browse files Browse the repository at this point in the history
  • Loading branch information
layershifter committed Jun 13, 2022
1 parent 82bd30d commit ff8a7c2
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 23 deletions.
23 changes: 23 additions & 0 deletions apps/ssr-tests-v9/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,26 @@ flowchart TB
b22-->b3
end
```

### `test`

Uses assets from the `build` step and runs them in a real browser to ensure that there are no errors in console related to SSR.

```shell
# yarn test
```

```mermaid
flowchart TB
subgraph Test
t1(Open a browser)
t2(Open a page and run JS)
t3(Ensure that console is empty)
t1-->t2
t2-->t3
end
```

#### Debugging

All assets are available in `./dist` folder that is available once `build` have been run. You can open `./dist/index.html` in any browser and debug relevant issues.
1 change: 1 addition & 0 deletions apps/ssr-tests-v9/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"code-style": "just-scripts code-style",
"lint": "just-scripts lint",
"test": "jest --passWithNoTests",
"posttest": "node -r @fluentui/scripts/ts-node-register ./src/test.ts",
"type-check": "tsc -b tsconfig.json"
},
"dependencies": {
Expand Down
111 changes: 111 additions & 0 deletions apps/ssr-tests-v9/src/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import chalk from 'chalk';
import * as fs from 'fs';
import * as path from 'path';
import { launch, Browser } from 'puppeteer';

import { hrToSeconds } from './utils/helpers';

class RenderError extends Error {}

async function startBrowser(): Promise<Browser> {
let browser;
let attempt = 1;

while (!browser) {
try {
browser = await launch();
} catch (err) {
if (attempt === 5) {
console.log(`Failed to launch a browser after 5 attempts...`);
throw err;
}

console.log('A browser failed to start, retrying...');
console.log(err);
attempt++;
}
}

return browser;
}

export async function runTest(browser: Browser, url: string): Promise<void> {
const page = await browser.newPage();
await page.setRequestInterception(true);

let error;

page.on('console', message => {
if (message.type() === 'error') {
// Ignoring network errors as we have an interceptor that prevents loading everything except our JS bundle
if (!message.text().includes('net::ERR_FAILED')) {
error = new RenderError(message.text());
}
}
});

page.on('request', request => {
// Our interceptor allows only our HTML and JS output
if (request.url() === url || request.url().endsWith('/out-esm.js')) {
return request.continue();
}

return request.abort();
});

page.on('pageerror', err => {
error = err;
});

await page.goto(url);
await page.close();

if (error) {
throw error;
}
}

async function test(): Promise<void> {
const startTime = process.hrtime();
console.log('Starting a browser...');

const browser = await startBrowser();
console.log('Using', await browser.version());

const htmlPath = path.resolve(__dirname, '..', 'dist', 'index.html');

if (!fs.existsSync(htmlPath)) {
throw new Error('"dist/index.html" does not exist, please run "yarn build" first');
}

const url = `file://${htmlPath}`;
console.log(`Using "${url}"`);

await runTest(browser, url);
await browser.close();

console.log(`Test finished successfully in ${hrToSeconds(process.hrtime(startTime))}`);
}

test().catch(err => {
console.log('');
console.log(chalk.bgRed.whiteBright(' @fluentui/ssr-tests-v9 '));

if (err instanceof RenderError) {
console.log(
[
' The test failed.',
'Please use `$ npx serve dist` or `$ open dist/index.html` to open a HTML page that is used in tests.',
].join(' '),
);
console.log(' The reference error is below, you will see it in Devtools on the opened page.');
console.log('');
} else {
console.log(' The test is failed, the error below contains relevant information.');
console.log('');
}

console.log(err);

process.exit(1);
});
16 changes: 15 additions & 1 deletion apps/ssr-tests-v9/src/utils/buildAssets.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import * as fs from 'fs';
import * as path from 'path';
import { launch } from 'puppeteer';
import * as tmp from 'tmp';

import { buildAssets } from './buildAssets';
import { buildAssets, getChromeVersion } from './buildAssets';

function stripComments(content: string): string {
return content.replace(/\/\/ .+/g, '');
}

describe('getChromeVersion', () => {
it('should return the same version as puppeteer', async () => {
const browser = await launch();

const rawVersion = await browser.version();
const version = rawVersion.split('/')[1].split('.')[0];

await browser.close();

expect(getChromeVersion()).toBe(version);
});
});

describe('buildAssets', () => {
it('compiles code to CJS & ESM', async () => {
const template = `export const Foo = 'foo'`;
Expand Down
9 changes: 8 additions & 1 deletion apps/ssr-tests-v9/src/utils/buildAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ type BuildConfig = {
esmOutfile: string;
};

/**
* Returns a version that matches bundled with puppeteer
*/
export function getChromeVersion(): string {
return '103';
}

export async function buildAssets(config: BuildConfig): Promise<void> {
const { cjsEntryPoint, cjsOutfile, esmEntryPoint, esmOutfile } = config;

Expand Down Expand Up @@ -47,7 +54,7 @@ export async function buildAssets(config: BuildConfig): Promise<void> {
require.resolve('../shims/module'),
],
format: 'iife',
target: 'chrome101',
target: `chrome${getChromeVersion()}`,
});
} catch (e) {
throw new Error(
Expand Down
1 change: 1 addition & 0 deletions apps/ssr-tests-v9/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"target": "ES2019",
"allowSyntheticDefaultImports": true,
"noEmit": true,
"isolatedModules": true,
"importHelpers": true,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@
"postcss-modules": "4.1.3",
"prettier": "2.2.1",
"pretty-bytes": "5.6.0",
"puppeteer": "14.3.0",
"raw-loader": "4.0.2",
"react": "16.14.0",
"react-app-polyfill": "2.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,17 @@ import type { SelectProps, SelectState } from './Select.types';
* @param ref - reference to the `<select>` element in Select
*/
export const useSelect_unstable = (props: SelectProps, ref: React.Ref<HTMLSelectElement>): SelectState => {
const { select, icon, root, appearance = 'outline', onChange, size = 'medium' } = props;
const {
defaultValue,
value,
select,
icon,
root,
appearance = 'outline',

onChange,
size = 'medium',
} = props;

const nativeProps = getPartitionedNativeProps({
props,
Expand All @@ -32,7 +42,9 @@ export const useSelect_unstable = (props: SelectProps, ref: React.Ref<HTMLSelect
select: resolveShorthand(select, {
required: true,
defaultProps: {
defaultValue,
ref,
value,
...nativeProps.primary,
},
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export const InitialValue = () => {
return (
<>
<label htmlFor={selectId}>Color</label>
<Select id={selectId}>
<option>Red</option>
<option selected>Green</option>
<option>Blue</option>
<Select id={selectId} defaultValue="green">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</Select>
</>
);
Expand Down
Loading

0 comments on commit ff8a7c2

Please sign in to comment.