-
-
Notifications
You must be signed in to change notification settings - Fork 372
test: Preact CLI tests #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
864e55c
8d5d286
a70e062
f0b2020
69829bb
7800035
2197b93
32d6cfb
8ae3a8d
266443b
332f858
24840c6
0706424
b6d179a
0eed33a
ddd4700
6a4b7e7
2546b75
f64422b
9b3bea3
17e8aa3
9395884
5c10410
cbe6052
7a3f7bd
bf1b859
1f74d82
ecb762e
4e0ec76
94038b7
59e1293
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| /node_modules | ||
| /lib | ||
| /examples/*/build | ||
| /tests/output | ||
| .DS_Store | ||
| npm-debug.log |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,11 @@ export default asyncCommand({ | |
| description: 'Pre-install SASS/SCSS support', | ||
| type: 'boolean', | ||
| default: false | ||
| }, | ||
| install: { | ||
| description: 'Install dependencies', | ||
| type: 'boolean', | ||
| default: true | ||
| } | ||
| }, | ||
|
|
||
|
|
@@ -112,34 +117,36 @@ export default asyncCommand({ | |
|
|
||
| spinner.text = 'Installing dev dependencies'; | ||
|
||
|
|
||
| await npm(target, [ | ||
| 'install', '--save-dev', | ||
| 'preact-cli', | ||
| 'if-env', | ||
| 'eslint', | ||
| 'eslint-config-synacor', | ||
|
|
||
| // install sass setup if --sass | ||
| ...(argv.sass ? [ | ||
| 'node-sass', | ||
| 'sass-loader' | ||
| ] : []), | ||
|
|
||
| // install less setup if --less | ||
| ...(argv.less ? [ | ||
| 'less', | ||
| 'less-loader' | ||
| ] : []) | ||
| ].filter(Boolean)); | ||
|
|
||
| spinner.text = 'Installing dependencies'; | ||
|
|
||
| await npm(target, [ | ||
| 'install', '--save', | ||
| 'preact', | ||
| 'preact-compat', | ||
| 'preact-router' | ||
| ]); | ||
| if (argv.install) { | ||
| await npm(target, [ | ||
| 'install', '--save-dev', | ||
| 'preact-cli', | ||
| 'if-env', | ||
| 'eslint', | ||
| 'eslint-config-synacor', | ||
|
|
||
| // install sass setup if --sass | ||
| ...(argv.sass ? [ | ||
| 'node-sass', | ||
| 'sass-loader' | ||
| ] : []), | ||
|
|
||
| // install less setup if --less | ||
| ...(argv.less ? [ | ||
| 'less', | ||
| 'less-loader' | ||
| ] : []) | ||
| ].filter(Boolean)); | ||
|
|
||
| spinner.text = 'Installing dependencies'; | ||
|
|
||
| await npm(target, [ | ||
| 'install', '--save', | ||
| 'preact', | ||
| 'preact-compat', | ||
| 'preact-router' | ||
| ]); | ||
| } | ||
|
|
||
| spinner.succeed('Done!\n'); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import test from 'tape'; | ||
|
|
||
| const defaultTestTimeout = 10 * 1000; | ||
|
|
||
| const asyncTest = (...args) => { | ||
| let { fn, tapeArgs, hasTimeout } = parseArgs(args); | ||
|
|
||
| test(...tapeArgs, (t) => { | ||
| if (!hasTimeout) t.timeoutAfter(defaultTestTimeout); | ||
| fn(t) | ||
| .then(() => t.end()) | ||
| .catch(e => t.end(e)); | ||
| }); | ||
| }; | ||
|
|
||
| asyncTest.only = (...args) => { | ||
| let { fn, tapeArgs, hasTimeout } = parseArgs(args); | ||
|
|
||
| test.only(...tapeArgs, t => { | ||
| if (!hasTimeout) t.timeoutAfter(defaultTestTimeout); | ||
| fn(t) | ||
| .then(() => t.end()) | ||
| .catch(e => t.end(e)); | ||
| }); | ||
| }; | ||
|
|
||
| export default asyncTest; | ||
|
|
||
| const parseArgs = args => { | ||
| let fn = args[args.length - 1]; | ||
| let tapeArgs = args.slice(0, args.length - 1); | ||
| let opts = tapeArgs[1]; | ||
|
|
||
| return { | ||
| fn, | ||
| tapeArgs, | ||
| hasTimeout: opts && opts.timeout !== undefined | ||
| }; | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| export const normalize = obj => { | ||
| let keys = Object.keys(obj); | ||
|
|
||
| if (keys.length === 1 && keys[0] === 'size' && typeof keys[0] === 'number') { | ||
| return { size: Math.round(obj.size / 10) * 10 }; | ||
| } | ||
|
|
||
| return keys.reduce((agg, key) => { | ||
| let newKey = key.replace(/\.chunk\.\w+\./, '.chunk.*.'); | ||
| agg[newKey] = normalize(obj[key]); | ||
| return agg; | ||
| }, {}); | ||
| }; | ||
|
|
||
| const smallBuildCommons = { | ||
| assets: { | ||
| 'favicon.ico': { size: 15086 }, | ||
| 'icon.png': { size: 51484 } | ||
| }, | ||
| 'polyfills.chunk.*.js': { size: 4068 }, | ||
| 'favicon.ico': { size: 15086 }, | ||
| 'sw.js': { size: 3378 }, | ||
| 'manifest.json': { size: 298 }, | ||
| 'push-manifest.json': { size: 2 }, | ||
| }; | ||
|
|
||
| const fullBuildCommons = { | ||
| assets: { | ||
| 'favicon.ico': { size: 15086 }, | ||
| icons: { | ||
| 'android-chrome-192x192.png': { size: 14058 }, | ||
| 'android-chrome-512x512.png': { size: 51484 }, | ||
| 'apple-touch-icon.png': { size: 12746 }, | ||
| 'favicon-16x16.png': { size: 626 }, | ||
| 'favicon-32x32.png': { size: 1487 }, | ||
| 'mstile-150x150.png': { size: 9050 } | ||
| } | ||
| }, | ||
| 'polyfills.chunk.*.js': { size: 4066 }, | ||
| 'push-manifest.json': { size: 303 }, | ||
| 'favicon.ico': { size: 15086 }, | ||
| 'manifest.json': { size: 426 }, | ||
| 'sw.js': { size: 3905 } | ||
| }; | ||
|
|
||
| export const expectedOutputs = normalize({ | ||
| empty: { | ||
| ...smallBuildCommons, | ||
| 'bundle.js': { size: 10694 }, | ||
| 'index.html': { size: 534 }, | ||
| 'style.css': { size: 131 }, | ||
| 'style.css.map': { size: 359 }, | ||
| }, | ||
| simple: { | ||
| ...smallBuildCommons, | ||
| 'bundle.js': { size: 11336 }, | ||
| 'index.html': { size: 548 }, | ||
| 'style.css': { size: 296}, | ||
| 'style.css.map': { size: 621 }, | ||
| }, | ||
| root: { | ||
| ...fullBuildCommons, | ||
| 'bundle.js': { size: 18739 }, | ||
| 'route-home.chunk.*.js': { size: 959 }, | ||
| 'route-profile.chunk.*.js': { size: 1595 }, | ||
| 'index.html': { size: 775 }, | ||
| 'style.css': { size: 1065 }, | ||
| 'style.css.map': { size: 2246 }, | ||
| }, | ||
| 'default': { | ||
| ...fullBuildCommons, | ||
| 'bundle.js': { size: 19661 }, | ||
| 'route-home.chunk.*.js': { size: 961 }, | ||
| 'route-profile.chunk.*.js': { size: 1597 }, | ||
| 'index.html': { size: 775 }, | ||
| 'style.css': { size: 1065 }, | ||
| 'style.css.map': { size: 2345 }, | ||
| } | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import test from './async-test'; | ||
| import { resolve } from 'path'; | ||
| import { create, build } from './lib/cli'; | ||
| import lsr from './lib/lsr'; | ||
| import { setup, clean } from './lib/output'; | ||
| import { normalize, expectedOutputs } from './build.snapshot'; | ||
|
|
||
| test('preact build - before', async () => { | ||
| await setup(); | ||
| }); | ||
|
|
||
| ['empty', 'simple', 'root', 'default'].forEach(template => | ||
| test(`preact build - should produce output. Veryfing ${template}`, async t => { | ||
| let app = await create('app', template); | ||
| await build(app); | ||
|
|
||
| let output = await lsr(resolve(app, 'build')); | ||
|
|
||
| t.isEquivalent(normalize(output), expectedOutputs[template]); | ||
| }) | ||
| ); | ||
|
|
||
| test('preact build - after', async () => { | ||
| await clean(); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import test from './async-test'; | ||
| import { resolve } from 'path'; | ||
| import { create } from './lib/cli'; | ||
| import lsr from './lib/lsr'; | ||
| import { setup, clean } from './lib/output'; | ||
|
|
||
| const listTemplate = async dir => await lsr(resolve(__dirname, '../examples', dir), ['.gitkeep', 'package.json']); | ||
| const listOutput = async dir => await lsr(dir, ['.gitkeep', 'package.json']); | ||
|
|
||
| test('preact create - before', async () => { | ||
| await setup(); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd also prefer to move all the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I separated them because it the 'main' test fails then they will be called for sure. It doesn't matter for I thought that consistency between all tests would be easier to maintain & extend. |
||
| }); | ||
|
|
||
| test('preact create - should create project using full template by default.', async t => { | ||
| let fullExample = await listTemplate('full'); | ||
| let app = await create('app'); | ||
| let generated = await listOutput(app); | ||
|
|
||
| t.isEquivalent(generated, fullExample); | ||
| }); | ||
|
|
||
| ['root', 'simple', 'empty'].forEach(template => | ||
| test(`preact create - should create project using provided template. Verifying ${template}`, async t => { | ||
| let example = await listTemplate(template); | ||
| let app = await create('app', template); | ||
| let generated = await listOutput(app); | ||
|
|
||
| t.isEquivalent(generated, example); | ||
| }) | ||
| ); | ||
|
|
||
| test('preact build - after', async () => { | ||
| await clean(); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import { Launcher } from 'lighthouse/chrome-launcher'; | ||
| import chrome from 'chrome-remote-interface'; | ||
|
|
||
| export default async () => { | ||
| let launcher = new Launcher({ | ||
| port: 9222, | ||
| autoSelectChrome: true, | ||
| additionalFlags: [ | ||
| '--window-size=1024,768', | ||
| '--disable-gpu', | ||
| '--headless' | ||
| ] | ||
| }); | ||
| await launcher.launch(); | ||
| let protocol = await setup(); | ||
| return { launcher, protocol }; | ||
| }; | ||
|
|
||
| export const waitUntil = async (Runtime, expression, retryCount = 10, retryInterval = 500) => { | ||
| if (retryCount < 0) { | ||
| throw new Error(`Wait until: '${expression}' timed out.`); | ||
| } | ||
|
|
||
| let { result } = await Runtime.evaluate({ expression }); | ||
|
|
||
| if (!result || !result.value) { | ||
| await delay(retryInterval); | ||
| await waitUntil(Runtime, expression, retryCount - 1, retryInterval); | ||
| } | ||
| }; | ||
|
|
||
| export const loadPage = async (chrome, url, retryCount = 10, retryInterval = 500) => { | ||
| let result = await openPage(chrome, url, retryCount, retryInterval); | ||
| await chrome.Page.loadEventFired(); | ||
| return result; | ||
| }; | ||
|
|
||
| const openPage = async (chrome, url, retryCount, retryInterval) => { | ||
| if (retryCount < 0) { | ||
| throw new Error('Page could not be loaded!'); | ||
| } | ||
|
|
||
| let { Network, Page } = chrome; | ||
| Page.navigate({ url }); | ||
|
|
||
| let result; | ||
|
|
||
| try { | ||
| result = await new Promise((resolve, reject) => { | ||
| Network.responseReceived((result) => { | ||
| let { status, url: responseUrl } = result.response; | ||
| if (responseUrl === url && status === 200) { | ||
| resolve(result); | ||
| } | ||
| }); | ||
|
|
||
| setTimeout(() => { | ||
| reject(); | ||
| }, retryInterval); | ||
| }); | ||
| } catch (e) { | ||
| result = await openPage(chrome, url, retryCount - 1, retryInterval); | ||
| } | ||
|
|
||
| return result; | ||
| }; | ||
|
|
||
| const delay = time => new Promise(r => setTimeout(() => r(), time)); | ||
|
|
||
| const setup = () => new Promise((resolve, reject) => { | ||
| chrome(protocol => { | ||
| const { Page, Runtime, Network } = protocol; | ||
|
|
||
| Promise.all([ | ||
| Page.enable(), | ||
| Runtime.enable(), | ||
| Network.enable(), | ||
| ]).then(() => { | ||
| resolve(protocol); | ||
| }) | ||
| .catch(reject); | ||
| }).on('error', err => reject(new Error('Cannot connect to Chrome:' + err))); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good call.