Skip to content

Commit

Permalink
feat(test): externalize jest helper proto-packages
Browse files Browse the repository at this point in the history
  • Loading branch information
Xunnamius committed Feb 19, 2021
1 parent efc916e commit 2dbfb8e
Show file tree
Hide file tree
Showing 4 changed files with 514 additions and 245 deletions.
137 changes: 52 additions & 85 deletions test/integration-node.test.ts
Original file line number Diff line number Diff line change
@@ -1,111 +1,78 @@
import { name as pkgName, version as pkgVersion, main as pkgMain } from '../package.json';
import { resolve } from 'path';
import sjx from 'shelljs';
import debugFactory from 'debug';
import uniqueFilename from 'unique-filename';
import del from 'del';
import { name as pkgName, version as pkgVersion, main as pkgMain } from '../package.json';
import {
run,
mockFixtureFactory,
dummyNpmPackageFixture,
npmLinkSelfFixture,
nodeImportTestFixture,
FixtureOptions
} from './setup';

const TEST_IDENTIFIER = 'integration-node';
const debug = debugFactory(`${pkgName}:${TEST_IDENTIFIER}`);

sjx.config.silent = !process.env.DEBUG;
const fullPkgMain = `${__dirname}/../${pkgMain}`;
const debug = debugFactory(`${pkgName}:${TEST_IDENTIFIER}`);

if (!sjx.test('-e', `${__dirname}/../${pkgMain}`)) {
debug(`unable to find main distributable: ${__dirname}/../${pkgMain}`);
throw new Error(
'must build distributables before running this test suite (try `npm run build-dist`)'
);
}
const fixtureOptions = {
initialFileContents: {
'package.json': `{"name":"dummy-pkg","dependencies":{"${pkgName}":"${pkgVersion}"}}`
} as FixtureOptions['initialFileContents'],
use: [dummyNpmPackageFixture(), npmLinkSelfFixture(), nodeImportTestFixture()]
};

debug(`pkgName: "${pkgName}"`);
debug(`pkgVersion: "${pkgVersion}"`);
const withMockedFixture = mockFixtureFactory(TEST_IDENTIFIER, fixtureOptions);

const nodeVersion = process.env.MATRIX_NODE_VERSION || process.version;
debug(`nodeVersion: "${nodeVersion}"`);

if (!nodeVersion) throw new Error('bad MATRIX_NODE_VERSION encountered');

const createIndexAndRunTest = (root: string) => ({ esm }: { esm: boolean }) => {
const ext = `${esm ? 'm' : ''}js`;
const runTest = async ({ esm }: { esm: boolean }) => {
const indexPath = `src/index.${esm ? 'm' : ''}js`;

const cmd = new sjx.ShellString(
// TODO: update file below to output "working" only with success condition
fixtureOptions.initialFileContents[indexPath] =
(esm
? `import { sum, diff, mult, div } from '${pkgName}';`
: `const { sum, diff, mult, div } = require('${pkgName}');`) +
`
`
const working = sum(2, 2) == 4 && diff(2, 2) == 0 && mult(2, 3) == 6 && div({ dividend: 4, divisor: 2 }) == 2;
console.log(working ? 'working' : 'not working');`.trim()
);
console.log(working ? 'working' : 'not working');`;

debug(`echoing string \`${cmd}\` to ${root}/index.${ext}`);
cmd.to(`${root}/index.${ext}`);

debug(`package.json contents: ${sjx.cat('package.json').stdout}`);

const exec = sjx.exec(`node --experimental-json-modules index.${ext}`);
const stdout = exec.stdout.trim();
const stderr = exec.stderr.trim();

if (esm) {
debug(`result: \`${stdout}\` (expected "working" or "")`);
debug(`result: \`${stderr}\` (expected "" or an error in a 3rd party dependency)`);
expect(stdout == 'working' || / \/.+\/node_modules\/.+$/m.test(stderr)).toBeTrue();
} else {
debug(`result: "${stdout}" (expected "working")`);
expect(stdout).toBe('working');
}
};
await withMockedFixture(async (ctx) => {
if (!ctx.testResult) throw new Error('must use node-import-test fixture');

let deleteRoot: () => Promise<void>;
let runTest: ReturnType<typeof createIndexAndRunTest>;
if (esm) {
debug('(expecting stdout to be "working" or "")');
debug('(expecting stderr to be "" or an error in a 3rd party dependency)');

beforeEach(async () => {
const root = uniqueFilename(sjx.tempdir(), TEST_IDENTIFIER);
const pkgJson = `${root}/package.json`;
expect(ctx.testResult.stdout).toBeOneOf(['working', '']);
ctx.testResult.stdout == '' &&
expect(ctx.testResult.stderr).toMatch(/ \/.+\/node_modules\/.+$/m);
} else {
debug('(expecting exit code to be 0)');
debug('(expecting stdout to be "working")');

deleteRoot = async () => {
sjx.cd('..');
debug(`forcibly removing dir ${root}`);
await del(root);
};

sjx.mkdir('-p', root);
sjx.mkdir('-p', `${root}/node_modules`);
pkgName.includes('/') &&
sjx.mkdir('-p', `${root}/node_modules/${pkgName.split('/')[0]}`);
const cd = sjx.cd(root);

if (cd.code != 0) {
throw new Error(`failed to mkdir/cd into ${root}: ${cd.stderr} ${cd.stdout}`);
} else debug(`created temp root dir: ${root}`);

new sjx.ShellString(
`{"name":"dummy-pkg","dependencies":{"${pkgName}":"${pkgVersion}"}}`
).to(pkgJson);
expect(ctx.testResult.code).toBe(0);
expect(ctx.testResult.stdout).toBe('working');
}
});

debug(`creating symbolic link`);
const makeLink = sjx.ln('-s', resolve(`${__dirname}/..`), `node_modules/${pkgName}`);
delete fixtureOptions.initialFileContents[indexPath];
};

if (makeLink.code !== 0) {
throw new Error(
`unable to create symbolic link: ${makeLink}\n\t${makeLink.stderr} ${makeLink.stdout}`
);
beforeAll(async () => {
if ((await run('test', ['-e', fullPkgMain])).code != 0) {
debug(`unable to find main distributable: ${fullPkgMain}`);
throw new Error('must build distributables first (try `npm run build-dist`)');
}

debug(`directory at this point: ${sjx.exec('tree -a', { silent: true }).stdout}`);
runTest = createIndexAndRunTest(root);
});

afterEach(() => deleteRoot());

describe(`${pkgName} [${TEST_IDENTIFIER}]`, () => {
it('works as an ESM import', async () => {
expect.hasAssertions();
runTest({ esm: true });
});
it('works as an ESM import', async () => {
expect.hasAssertions();
await runTest({ esm: true });
});

it('works as a CJS require(...)', async () => {
expect.hasAssertions();
runTest({ esm: false });
});
it('works as a CJS require(...)', async () => {
expect.hasAssertions();
await runTest({ esm: false });
});
197 changes: 70 additions & 127 deletions test/integration-webpack.test.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
import { name as pkgName, version as pkgVersion, main as pkgMain } from '../package.json';
import { resolve } from 'path';
import sjx from 'shelljs';
import debugFactory from 'debug';
import uniqueFilename from 'unique-filename';
import del from 'del';
import { name as pkgName, version as pkgVersion, main as pkgMain } from '../package.json';
import {
run,
mockFixtureFactory,
dummyNpmPackageFixture,
webpackTestFixture,
FixtureOptions,
npmLinkSelfFixture
} from './setup';

const TEST_IDENTIFIER = 'integration-webpack';
const debug = debugFactory(`${pkgName}:${TEST_IDENTIFIER}`);

sjx.config.silent = !process.env.DEBUG;

if (!sjx.test('-e', `${__dirname}/../${pkgMain}`)) {
debug(`unable to find main distributable: ${__dirname}/../${pkgMain}`);
throw new Error(
'must build distributables before running this test suite (try `npm run build-dist`)'
);
}

debug(`pkgName: "${pkgName}"`);
debug(`pkgVersion: "${pkgVersion}"`);
const fullPkgMain = `${__dirname}/../${pkgMain}`;
const debug = debugFactory(`${pkgName}:${TEST_IDENTIFIER}`);

const webpackVersion = process.env.MATRIX_WEBPACK_VERSION || 'latest';
debug(`webpackVersion: "${webpackVersion}"`);

if (!webpackVersion) throw new Error('bad MATRIX_WEBPACK_VERSION encountered');
const fixtureOptions = {
webpackVersion,
initialFileContents: {
'package.json': `{"name":"dummy-pkg","dependencies":{"${pkgName}":"${pkgVersion}"}}`
} as FixtureOptions['initialFileContents'],
use: [dummyNpmPackageFixture(), npmLinkSelfFixture(), webpackTestFixture()]
};

const withMockedFixture = mockFixtureFactory(TEST_IDENTIFIER, fixtureOptions);

enum SourceType {
CJS = 'cjs',
Expand All @@ -35,128 +37,69 @@ enum DestType {
CJS_LIB = 'cjs-library'
}

const createIndexAndRunTest = (root: string) => ({
source,
dest
}: {
source: SourceType;
dest: DestType;
}) => {
const runTest = async ({ source, dest }: { source: SourceType; dest: DestType }) => {
const ext = `${source == SourceType.ESM ? 'm' : ''}js`;
const indexPath = `src/index.${ext}`;

const cmd1 = new sjx.ShellString(
// TODO: update file below to output "working" only with success condition
fixtureOptions.initialFileContents[indexPath] =
(source == SourceType.ESM
? `import { sum, diff, mult, div } from '${pkgName}';`
: `const { sum, diff, mult, div } = require('${pkgName}');`) +
`
const working = sum(2, 2) == 4 && diff(2, 2) == 0 && mult(2, 3) == 6 && div({ dividend: 4, divisor: 2 }) == 2;
console.log(working ? 'working' : 'not working');`.trim()
);

debug(`echoing string \`${cmd1}\` to ${root}/src/index.${ext}`);
cmd1.to(`${root}/src/index.${ext}`);

const cmd2 = new sjx.ShellString(
`
module.exports = {
name: 'dummy',
mode: 'production',
target: 'node',
node: false,
entry: \`\${__dirname}/src/index.${ext}\`,
output: {
filename: 'index.js',
path: \`\${__dirname}/dist\`,
${dest == DestType.CJS_LIB ? "libraryTarget: 'commonjs2'" : ''}
}
}`.trim()
);

debug(`echoing string \`${cmd2}\` to ${root}/webpack.config.js`);
cmd2.to(`${root}/webpack.config.js`);

debug(`directory at this point: ${sjx.exec('tree -a', { silent: true }).stdout}`);

sjx.exec(`npm install webpack@${webpackVersion} webpack-cli`);

debug(`package.json contents: ${sjx.cat('package.json').stdout}`);

const webpack = sjx.exec('npx webpack');

debug(`webpack run: (${webpack.code})\n${webpack.stderr}\n${webpack.stdout}`);
expect(webpack.code).toBe(0);
const working = sum(2, 2) == 4 && diff(2, 2) == 0 && mult(2, 3) == 6 && div({ dividend: 4, divisor: 2 }) == 2;
console.log(working ? 'working' : 'not working');`;

fixtureOptions.initialFileContents['webpack.config.js'] = `
module.exports = {
name: 'dummy',
mode: 'production',
target: 'node',
node: false,
entry: \`\${__dirname}/src/index.${ext}\`,
output: {
filename: 'index.js',
path: \`\${__dirname}/dist\`,
${dest == DestType.CJS_LIB ? "libraryTarget: 'commonjs2'" : ''}
}
}`;

await withMockedFixture(async (ctx) => {
if (!ctx.testResult) throw new Error('must use webpack-test fixture');

debug('(expecting exit code to be 0)');
debug('(expecting stdout to be "working")');

expect(ctx.testResult.code).toBe(0);
expect(ctx.testResult.stdout).toBe('working');
});

const result = sjx.exec(`node ${root}/dist/index.js`).stdout.trim();
debug(`result: "${result}" (expected "working")`);
expect(result).toBe('working');
delete fixtureOptions.initialFileContents[indexPath];
};

let deleteRoot: () => Promise<void>;
let runTest: ReturnType<typeof createIndexAndRunTest>;

beforeEach(async () => {
const root = uniqueFilename(sjx.tempdir(), TEST_IDENTIFIER);
const pkgJson = `${root}/package.json`;

deleteRoot = async () => {
sjx.cd('..');
debug(`forcibly removing dir ${root}`);
await del(root);
};

sjx.mkdir('-p', root);
sjx.mkdir('-p', `${root}/src`);
sjx.mkdir('-p', `${root}/node_modules`);
pkgName.includes('/') &&
sjx.mkdir('-p', `${root}/node_modules/${pkgName.split('/')[0]}`);

const cd = sjx.cd(root);

if (cd.code != 0) {
await deleteRoot();
throw new Error(`failed to mkdir/cd into ${root}: ${cd.stderr} ${cd.stdout}`);
} else debug(`created temp root dir: ${root}`);

new sjx.ShellString(
`{"name":"dummy-pkg","dependencies":{"${pkgName}":"${pkgVersion}"}}`
).to(pkgJson);

debug(`creating symbolic link`);
const makeLink = sjx.ln('-s', resolve(`${__dirname}/..`), `node_modules/${pkgName}`);

if (makeLink.code !== 0) {
throw new Error(
`unable to create symbolic link: ${makeLink}\n\t${makeLink.stderr} ${makeLink.stdout}`
);
beforeAll(async () => {
if ((await run('test', ['-e', fullPkgMain])).code != 0) {
debug(`unable to find main distributable: ${fullPkgMain}`);
throw new Error('must build distributables first (try `npm run build-dist`)');
}

runTest = createIndexAndRunTest(root);
});

afterEach(() => deleteRoot());

describe(`${pkgName} [${TEST_IDENTIFIER}]`, () => {
// eslint-disable-next-line jest/lowercase-name
it('CJS source can be bundled into CJS app by webpack', async () => {
expect.hasAssertions();
runTest({ source: SourceType.CJS, dest: DestType.CJS });
});
it('can be bundled as CJS source into CJS app by webpack', async () => {
expect.hasAssertions();
await runTest({ source: SourceType.CJS, dest: DestType.CJS });
});

// eslint-disable-next-line jest/lowercase-name
it('CJS source can be bundled into CJS library by webpack', async () => {
expect.hasAssertions();
runTest({ source: SourceType.CJS, dest: DestType.CJS_LIB });
});
it('can be bundled as CJS source into CJS library by webpack', async () => {
expect.hasAssertions();
await runTest({ source: SourceType.CJS, dest: DestType.CJS_LIB });
});

// eslint-disable-next-line jest/lowercase-name
it('ESM source can be bundled into CJS app by webpack', async () => {
expect.hasAssertions();
runTest({ source: SourceType.ESM, dest: DestType.CJS });
});
it('can be bundled as ESM source into CJS app by webpack', async () => {
expect.hasAssertions();
await runTest({ source: SourceType.ESM, dest: DestType.CJS });
});

// eslint-disable-next-line jest/lowercase-name
it('ESM source can be bundled into CJS library by webpack', async () => {
expect.hasAssertions();
runTest({ source: SourceType.ESM, dest: DestType.CJS_LIB });
});
it('can be bundled as ESM source into CJS library by webpack', async () => {
expect.hasAssertions();
await runTest({ source: SourceType.ESM, dest: DestType.CJS_LIB });
});

0 comments on commit 2dbfb8e

Please sign in to comment.