Skip to content

Commit

Permalink
Test "yarn start" (#1828)
Browse files Browse the repository at this point in the history
* mod: Use execa

* mod: Connect IO to the front process by default

* Test "yarn start"

* "starts GraphiQL" should fail. Should downgrade express-graphql to 0.8.9.

* Downgrade express-graphql for quick fix for #1822
  • Loading branch information
piglovesyou authored Nov 19, 2019
1 parent 364cd04 commit 3477228
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 41 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ module.exports = {
'prettier/react',
],

plugins: ['flowtype', 'css-modules', 'prettier'],
plugins: ['flowtype', 'css-modules', 'prettier', 'jest'],

globals: {
__DEV__: true,
},

env: {
browser: true,
jest: true,
},

rules: {
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"classnames": "^2.2.6",
"cookie-parser": "^1.4.3",
"express": "^4.16.3",
"express-graphql": "^0.9.0",
"express-graphql": "~0.8.0",
"express-jwt": "^5.3.1",
"graphql": "^14.5.8",
"history": "^4.7.2",
Expand Down Expand Up @@ -68,9 +68,11 @@
"eslint-plugin-css-modules": "^2.9.1",
"eslint-plugin-flowtype": "^3.13.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jest": "^23.0.4",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-react": "^7.11.1",
"execa": "^3.3.0",
"file-loader": "^4.2.0",
"flow-bin": "^0.112.0",
"front-matter": "^3.0.2",
Expand All @@ -79,6 +81,7 @@
"identity-obj-proxy": "^3.0.0",
"jest": "^24.9.0",
"lint-staged": "^9.4.3",
"log-symbols": "^3.0.0",
"markdown-it": "^10.0.0",
"mkdirp": "^0.5.1",
"null-loader": "^3.0.0",
Expand All @@ -93,6 +96,7 @@
"postcss-preset-env": "^6.6.0",
"postcss-pseudoelements": "^5.0.0",
"prettier": "^1.19.1",
"puppeteer": "^2.0.0",
"raw-loader": "^3.1.0",
"react-deep-force-update": "^2.1.3",
"react-dev-utils": "^9.1.0",
Expand All @@ -103,7 +107,9 @@
"stylelint-config-standard": "^19.0.0",
"stylelint-order": "^3.1.1",
"svg-url-loader": "^3.0.2",
"terminate": "^2.1.2",
"url-loader": "^2.2.0",
"wait-on": "^3.3.0",
"webpack": "^4.19.1",
"webpack-assets-manifest": "^3.0.2",
"webpack-bundle-analyzer": "^3.3.2",
Expand Down
79 changes: 79 additions & 0 deletions tools/__tests__/start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import assert from 'assert';
import path from 'path';
import { readFile, writeFile } from '../lib/fs';
import {
killApp,
waitApp,
timeout,
openBrowser,
getUrl,
} from '../lib/test-fns';
import { spawn } from '../lib/cp';

const startApp = (cwd, port) =>
spawn('yarn', ['start', '--silent'], {
cwd,
env: { PORT: String(port) },
});

describe('yarn start', () => {
const port = 3033;
const cwd = path.resolve(__dirname, '../..');
const app = startApp(cwd, port);
let browser;
let page;

beforeAll(async () => {
await waitApp(port);
[browser, page] = await openBrowser(port);
}, 60 * 1000);

afterAll(async () => {
await browser.close();
await killApp(app);
});

it('launches the App', async () => {
const expect = 'React.js News';
const actual = await page.$$eval('h1', es => es[1].textContent);
assert.deepStrictEqual(actual, expect);
});

it(
'does Hot Module Reload',
async () => {
const sourcePath = 'src/routes/home/Home.js';
const sourceAbsPath = path.join(cwd, sourcePath);
const expect = 'HMR!!!';
const defaultH1 = '<h1>React.js News</h1>';
const modifiedH1 = `<h1>${expect}</h1>`;

const modifySource = async () => {
const content = await readFile(sourceAbsPath);
if (!content.includes(defaultH1))
throw new Error('This test cannot run. Check "defaultH1".');
await writeFile(sourceAbsPath, content.replace(defaultH1, modifiedH1));
};

const resetSource = async () => {
const content = await readFile(sourceAbsPath);
await writeFile(sourceAbsPath, content.replace(modifiedH1, defaultH1));
};

await modifySource();
await timeout(3 * 1000);
const actual = await page.$$eval('h1', es => es[1].textContent);
assert.deepStrictEqual(actual, expect);

await resetSource();
},
30 * 1000,
);

it('starts GraphiQL', async () => {
const expect = 'GraphiQL';
await page.goto(`${getUrl(port)}/graphql`);
const actual = await page.$eval('.title', e => e.textContent);
assert.deepStrictEqual(actual, expect);
});
});
5 changes: 1 addition & 4 deletions tools/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,7 @@ const remote = {
// website: `http://<app>.azurewebsites.net`,
// };

const options = {
cwd: path.resolve(__dirname, '../build'),
stdio: ['ignore', 'inherit', 'inherit'],
};
const options = { cwd: path.resolve(__dirname, '../build') };

/**
* Deploy the contents of the `/build` folder to a remote server via Git.
Expand Down
33 changes: 18 additions & 15 deletions tools/lib/cp.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,31 @@
*/

import cp from 'child_process';
import execa from 'execa';

export const spawn = (command, args, options) =>
new Promise((resolve, reject) => {
cp.spawn(command, args, options).on('close', code => {
if (code === 0) {
resolve();
} else {
reject(new Error(`${command} ${args.join(' ')} => ${code} (error)`));
}
});
execa(command, args, {
stdio: ['ignore', 'inherit', 'inherit'],
...options,
});

export const exec = (command, options) =>
new Promise((resolve, reject) => {
cp.exec(command, options, (err, stdout, stderr) => {
if (err) {
reject(err);
return;
}
cp.exec(
command,
{
stdio: ['ignore', 'inherit', 'inherit'],
...options,
},
(err, stdout, stderr) => {
if (err) {
reject(err);
return;
}

resolve({ stdout, stderr });
});
resolve({ stdout, stderr });
},
);
});

export default { spawn, exec };
50 changes: 50 additions & 0 deletions tools/lib/test-fns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import terminate from 'terminate';
import i from 'log-symbols';
import waitOn from 'wait-on';
import puppeteer from 'puppeteer';

export function success(...args) {
return console.info(i.success, ...args);
}

export function info(...args) {
return console.info(i.info, ...args);
}

export function getUrl(port) {
return `http://localhost:${port}`;
}

export async function waitApp(port) {
const url = getUrl(port);
await waitOn({
resources: [url],
timeout: 60 * 1000,
});
}

export async function killApp(app) {
info(`Terminating app ${app.pid}...`);
await new Promise((resolve, reject) => {
terminate(app.pid, err => {
if (err) return reject(err);
return resolve();
});
});
success(`App ${app.pid} was terminated`);
}

export function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

export async function openBrowser(port) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
page.on('pageerror', err => {
throw new Error(`The page got an error: ${err}`);
});
const url = getUrl(port);
await page.goto(url);
return [browser, page];
}
3 changes: 3 additions & 0 deletions tools/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ async function start() {
appPromiseIsResolved = true;
appPromiseResolve();

const port = process.env.PORT ? Number(process.env.PORT) : undefined;

// Launch the development server with Browsersync and HMR
await new Promise((resolve, reject) =>
browserSync.create().init(
Expand All @@ -220,6 +222,7 @@ async function start() {
middleware: [server],
open: !process.argv.includes('--silent'),
...(isDebug ? {} : { notify: false, ui: false }),
...(port ? { port } : null),
},
(error, bs) => (error ? reject(error) : resolve(bs)),
),
Expand Down
Loading

0 comments on commit 3477228

Please sign in to comment.