Skip to content

Commit

Permalink
test: Add CLI tests for all subcommands & most of their flags (#1738)
Browse files Browse the repository at this point in the history
* test: Add CLI tests for all commands & most flags

* chore: Remove commented-out `watch` tests
  • Loading branch information
rschristian committed Nov 19, 2022
1 parent 735b72e commit 12e230d
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 100 deletions.
22 changes: 14 additions & 8 deletions packages/cli/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
declare global {
const __webpack_public_path__: string;
namespace jest {
interface Matchers<R> {
toBeCloseInSize(receivedSize: number, expectedSize: number): R;
toFindMatchingKey(receivedKey: string): R;
}
declare const __webpack_public_path__: string;

declare namespace jest {
interface Matchers<R> {
toBeCloseInSize(receivedSize: number, expectedSize: number): R;
toFindMatchingKey(receivedKey: string): R;
}
}

export {};
// Modified from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/shelljs/index.d.ts
declare module 'shelljs' {
const shell: {
cd: (string) => void;
exec: (string) => { stdout: string; stderr: string; code: number };
};
export = shell;
}
7 changes: 0 additions & 7 deletions packages/cli/tests/build.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,6 @@ describe('preact build', () => {
it('builds the `typescript` template', async () => {
let dir = await create('typescript');

// The tsconfig.json in the template covers the test directory,
// so TS will error out if it can't find even test-only module definitions
shell.cd(dir);
//shell.exec('npm i @types/enzyme@3.10.11 enzyme-adapter-preact-pure');
// Remove when https://github.com/preactjs/enzyme-adapter-preact-pure/issues/161 is resolved
shell.exec('rm tsconfig.json');

await expect(build(dir)).resolves.not.toThrow();
});

Expand Down
54 changes: 35 additions & 19 deletions packages/cli/tests/create.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const { readFile } = require('fs').promises;
const { relative, resolve } = require('path');
const { access, readFile } = require('fs').promises;
const { join, relative } = require('path');
const { create } = require('./lib/cli');
const { expand } = require('./lib/utils');
const snapshots = require('./images/create');
const shell = require('shelljs');

describe('preact create', () => {
it(`scaffolds the 'default' official template`, async () => {
it('scaffolds the `default` official template', async () => {
let dir = await create('default');

let output = await expand(dir).then(arr => {
Expand All @@ -15,29 +16,44 @@ describe('preact create', () => {
expect(output.sort()).toEqual(snapshots.default);
});

it(`should use template.html from the github repo`, async () => {
it('should use template.html from the github repo', async () => {
let dir = await create('netlify');

const templateFilePath = resolve(__dirname, dir, 'src', 'template.html');
const template = await readFile(templateFilePath, 'utf8');

const template = await readFile(join(dir, 'src/template.html'), 'utf8');
expect(template.includes('twitter:card')).toEqual(true);
});

it(`should have 'apple-touch-icon' meta tag`, async () => {
let dir = await create('simple');
describe('CLI Options', () => {
it('--name', async () => {
let dir = await create('simple', { name: 'renamed' });
const packageJson = await readFile(join(dir, 'package.json'), 'utf8');

const templateFilePath = resolve(__dirname, dir, 'src', 'template.html');
const template = await readFile(templateFilePath, 'utf8');
expect(JSON.parse(packageJson).name).toBe('renamed');

expect(template.includes('apple-touch-icon')).toEqual(true);
});
// @ts-ignore
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
await create('simple', { name: '*()@!#!$-Invalid-Name' });
expect(mockExit).toHaveBeenCalledWith(1);
mockExit.mockRestore();
});

it('should fail given an invalid name', async () => {
// @ts-ignore
const exit = jest.spyOn(process, 'exit').mockImplementation(() => {});
await create('simple', '*()@!#!$-Invalid-Name');
it('--git', async () => {
let dir = await create('simple', { git: true });
expect(await access(join(dir, '.git'))).toBeUndefined();

expect(exit).toHaveBeenCalledWith(1);
dir = await create('simple', { git: false });
await expect(access(join(dir, '.git'))).rejects.toThrow(
'no such file or directory'
);
});

it('--invalid-arg', () => {
const { code, stderr } = shell.exec(
`node ${join(__dirname, '../src/index.js')} create --invalid-arg`
);
expect(stderr).toMatch(
"Invalid argument '--invalid-arg' passed to create."
);
expect(code).toBe(1);
});
});
});
2 changes: 1 addition & 1 deletion packages/cli/tests/images/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ exports.template = `
<head>
<meta charset="utf-8">
<title>preact-custom-template</title>
<meta name="example-meta" content="Hello World">
<meta name="example-meta" content="Hello Prod">
<link rel="manifest" href="/manifest.json">
</head>
<body>
Expand Down
24 changes: 24 additions & 0 deletions packages/cli/tests/info.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const { join } = require('path');
const shell = require('shelljs');
const { subject } = require('./lib/output');

describe('preact info', () => {
it('--src', async () => {
let dir = await subject('minimal');
// env-info does not pick up on symlinks, so we need to install deps
shell.exec(`npm --prefix ${dir} i preact preact-render-to-string`);

const _cwd = process.cwd();

shell.cd(dir);
const { code, stdout } = shell.exec(
`node ${join(__dirname, '../src/index.js')} info`
);
['OS', 'Node', 'preact', 'preact-render-to-string'].forEach(label => {
expect(stdout).toMatch(`${label}:`);
});
expect(code).toBe(0);

shell.cd(_cwd);
});
});
65 changes: 29 additions & 36 deletions packages/cli/tests/lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,49 @@
const { join } = require('path');
const { mkdir, symlink } = require('fs').promises;
const { mkdir } = require('fs').promises;
const shell = require('shelljs');
const cmd = require('../../src/commands');
const { tmpDir } = require('./output');
const shell = require('shelljs');
const { linkPackage } = require('./utils');

const root = join(__dirname, '../../../..');

async function linkPackage(name, from, to) {
try {
await symlink(
join(from, 'node_modules', name),
join(to, 'node_modules', name)
);
} catch {}
}

const argv = {
_: [],
src: 'src',
dest: 'build',
config: 'preact.config.js',
prerenderUrls: 'prerender-urls.json',
'inline-css': true,
};

exports.create = async function (template, name) {
exports.create = async function (template, options) {
let dest = await tmpDir();
name = name || `test-${template}`;

await cmd.create(template, dest, { name, cwd: '.' });
let opts = Object.assign({ name: `test-${template}`, cwd: '.' }, options);
await cmd.create(template, dest, opts);

return dest;
};

exports.build = async function (cwd, options, installNodeModules = false) {
const argv = {
src: 'src',
dest: 'build',
config: 'preact.config.js',
prerenderUrls: 'prerender-urls.json',
'inline-css': true,
};

if (!installNodeModules) {
await mkdir(join(cwd, 'node_modules'), { recursive: true }); // ensure exists, avoid exit()
await linkPackage('preact', root, cwd);
await linkPackage('preact-render-to-string', root, cwd);
await linkPackage('preact', cwd);
await linkPackage('preact-render-to-string', cwd);
} else {
shell.cd(cwd);
shell.exec('npm i');
shell.exec(`npm --prefix ${cwd} i`);
}

let opts = Object.assign({}, { cwd }, argv, options);
let opts = Object.assign({ cwd }, argv, options);
return await cmd.build(opts.src, opts);
};

exports.watch = function (cwd, port, host = '127.0.0.1') {
const args = { ...argv };
delete args.dest;
delete args['inline-css'];
let opts = Object.assign({ cwd, host, port, https: false }, args);
return cmd.watch(argv.src, opts);
exports.watch = function (cwd, options) {
const argv = {
src: 'src',
host: '127.0.0.1',
https: false,
config: 'preact.config.js',
prerenderUrls: 'prerender-urls.json',
};

let opts = Object.assign({ cwd }, argv, options);
return cmd.watch(opts.src, opts);
};
15 changes: 13 additions & 2 deletions packages/cli/tests/lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-console */
const { relative, resolve } = require('path');
const { stat, readFile, writeFile } = require('fs').promises;
const { join, relative, resolve } = require('path');
const { stat, symlink, readFile, writeFile } = require('fs').promises;
const pRetry = require('p-retry');
const { promisify } = require('util');
const glob = promisify(require('glob').glob);
Expand Down Expand Up @@ -61,6 +61,16 @@ function waitUntil(action, errorMessage) {

const sleep = promisify(setTimeout);

async function linkPackage(name, cwd) {
const root = join(__dirname, '../../../..');
try {
await symlink(
join(root, 'node_modules', name),
join(cwd, 'node_modules', name)
);
} catch {}
}

expect.extend({
toFindMatchingKey(key, matchingKey) {
if (matchingKey) {
Expand Down Expand Up @@ -109,4 +119,5 @@ module.exports = {
log,
waitUntil,
sleep,
linkPackage,
};
21 changes: 21 additions & 0 deletions packages/cli/tests/list.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { join } = require('path');
const shell = require('shelljs');

describe('preact list', () => {
it('lists the official templates', () => {
const { code, stdout } = shell.exec(
`node ${join(__dirname, '../src/index.js')} list`
);
[
'default',
'netlify',
'simple',
'typescript',
'widget',
'widget-typescript',
].forEach(repoName => {
expect(stdout).toMatch(new RegExp(`${repoName}.* -`, 'g'));
});
expect(code).toBe(0);
});
});
4 changes: 3 additions & 1 deletion packages/cli/tests/subjects/custom-template/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
<meta charset="utf-8">
<title><% preact.title %></title>
<% if (htmlWebpackPlugin.options.config.isProd) { %>
<meta name="example-meta" content="Hello World">
<meta name="example-meta" content="Hello Prod">
<% } else { %>
<meta name="example-meta" content="Hello Dev">
<% } %>
<% preact.headEnd %>
</head>
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/tests/subjects/custom-webpack/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { h } from 'preact';

export default () => <h2>This is an app with custom template</h2>;
export default () => <h2>This is an app with custom webpack config</h2>;
8 changes: 6 additions & 2 deletions packages/cli/tests/subjects/custom-webpack/preact.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module.exports = function (config) {
config.output.filename = '[name].js';
module.exports = function (config, env) {
if (env.isProd) {
config.output.filename = '[name].js';
} else {
config.output.filename = 'renamed-bundle.js';
}
};

0 comments on commit 12e230d

Please sign in to comment.