knex/test/cli/esm-interop.spec.js
2022-04-21 19:05:40 +02:00

755 lines
20 KiB
JavaScript

'use strict';
const assert = require('assert');
const path = require('path');
const fs = require('fs');
const { execCommand } = require('cli-testlab');
const sqlite3 = require('sqlite3');
const semver = require('semver');
const { createTable } = require('./cli-test-utils');
const KNEX = path.normalize(__dirname + '/../../bin/cli.js');
const TEST_BASE = '../test/jake-util';
const fixture = [
/** MIGRATIONS */
{
title: 'migrates esm modules',
testCase: 'knexfile-esm',
knexArgs: ['migrate:latest', '--esm'],
dropDb: true,
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
{
title: 'migrates esm modules from a module',
testCase: 'knexfile-esm-module',
knexArgs: ['migrate:latest'],
expectedOutput: 'Batch 1 run: 1 migrations',
dropDb: true,
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
{
title: 'migrate esm module without --esm flag from a module',
testCase: 'knexfile-esm-module',
knexArgs: ['migrate:latest'],
dropDb: true,
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
{
title: 'migrates mjs modules',
testCase: 'knexfile-mjs',
knexfile: 'knexfile.mjs',
knexArgs: ['migrate:latest'],
dropDb: true,
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
/** SEEDS */
{
title: 'seeds esm files',
testCase: 'knexfile-esm',
expectedOutput: 'Ran 1 seed files',
knexArgs: ['seed:run', '--esm'],
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: 'seeds esm files from module',
testCase: 'knexfile-esm-module',
expectedOutput: 'Ran 1 seed files',
knexArgs: ['seed:run'],
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
dropDb: true,
},
{
title: 'seeds esm files from module without --esm flag',
testCase: 'knexfile-esm-module',
expectedOutput: 'Ran 1 seed files',
knexArgs: ['seed:run'],
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
dropDb: true,
},
{
title: 'seeds mjs files',
testCase: 'knexfile-mjs',
knexfile: 'knexfile.mjs',
knexArgs: ['seed:run'],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
/** SPECIAL CASES */
{
title: 'mjs files with mjs top level imports',
testCase: 'knexfile-imports',
knexfile: 'knexfile.mjs',
knexArgs: ['migrate:latest'],
dropDb: true,
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
},
{
title:
'Directory import is not supported, resolving ES modules imported from knexfile.mjs',
testCase: 'knexfile-imports',
knexfile: 'knexfile1.mjs',
knexArgs: ['migrate:latest'],
expectedErrorMessage: `Error [ERR_UNSUPPORTED_DIR_IMPORT]`,
},
{
title: 'dynamic importing js file from NON module package is not supported',
testCase: 'knexfile-imports',
knexfile: 'knexfile2.mjs',
knexArgs: ['migrate:latest'],
expectedErrorMessage: semver.eq(process.version, 'v14.13.0')
? 'Unexpected export statement in CJS module'
: semver.gte(process.version, 'v14.14.0')
? 'Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.'
: "Unexpected token 'export'",
},
{
title: 'static importing js file from NON module package is not supported',
testCase: 'knexfile-imports',
knexfile: 'knexfile2.mjs',
knexArgs: ['migrate:latest'],
expectedErrorMessage: semver.eq(process.version, 'v14.13.0')
? 'Unexpected export statement in CJS module'
: semver.gte(process.version, 'v14.14.0')
? 'Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.'
: "Unexpected token 'export'",
},
{
//Example: external module.type='module' by url 'packane-name/index.js'
title: 'can dynamically import external ESM module package by URL',
testCase: 'knexfile-imports',
knexfile: 'knexfile4.mjs',
knexArgs: ['migrate:latest'],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Importing commonjs from a mjs module',
testCase: 'knexfile-imports',
knexfile: 'knexfile5.mjs',
knexArgs: ['migrate:latest'],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Importing commonjs from a js ESM module and --esm interop',
testCase: 'knexfile-imports',
knexfile: 'knexfile6.js',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Importing js ESM from js ESM with --esm interop',
testCase: 'knexfile-imports',
knexfile: 'knexfile7.js',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Dynamic importing js ESM from js ESM with --esm interop',
testCase: 'knexfile-imports',
knexfile: 'knexfile8.js',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Static top level cjs import from js ESM with --esm interop',
testCase: 'knexfile-imports',
knexfile: 'knexfile9.js',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Dynamic ESM js import from commonjs/js with esm migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile10.js',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Imports commonjs/cjs provides js/esm migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile11.js',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'Imports commonjs/cjs provides cjs migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile11.js',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'cjs knexfile Imports commonjs/cjs provides cjs migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile12.cjs',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'cjs knexfile Imports commonjs/js provides js migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile13.cjs',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'cjs knexfile provides esm migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile14.cjs',
knexArgs: ['migrate:latest', `--esm`],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'cjs knexfile provides mjs migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile15.cjs',
knexArgs: ['migrate:latest', '--esm'],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'mjs knexfile provides cjs migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile16.mjs',
knexArgs: ['migrate:latest'],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
/**
* This is Standard NODEJS resolution
* */
title: 'mjs knexfile provides ESM/js migrations #1',
testCase: 'knexfile-imports',
knexfile: 'knexfile17.mjs',
knexArgs: ['migrate:latest'],
/**
* Migration DOESN'T RUN?, files aren't found
* config.migrations.loadExtensions defaults to ['.mjs']
*/
expectedOutput: 'Already up to date',
/** confirmation, migration didn't run */
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
], //schema is default knex schema
dropDb: true,
},
{
/**
* This is Standard NODEJS resolution
* even with the 'esm' module loader, AKA --esm
* no 'esm' involved, knexfile.mjs is navite/'imported'
*/
title: 'mjs knexfile provides ESM/js migrations #2',
testCase: 'knexfile-imports',
knexfile: 'knexfile17.mjs',
knexArgs: ['migrate:latest'],
/**
* Migration DOESN'T RUN, files aren't found
* config.migrations.loadExtensions defaults to ['.mjs']
*/
expectedOutput: 'Already up to date',
/** confirmation, migration didn't run */
expectedSchema: [
//schema is default knex schema
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
],
dropDb: true,
},
{
title: 'mjs knexfile provides ESM/js migrations if .js in loadExtensions',
testCase: 'knexfile-imports',
knexfile: 'knexfile18.mjs',
knexArgs: ['migrate:latest', '--esm'],
dropDb: true,
},
{
title:
"mjs knexfile CAN'T provide ESM/js migrations if .js in loadExtensions without --esm",
testCase: 'knexfile-imports',
knexfile: 'knexfile18.mjs',
knexArgs: ['migrate:latest'],
// Fails on NODE 12 & 14
expectedErrorMessage: "Unexpected token 'export'",
dropDb: true,
},
{
title: 'ESM/js knexfile provides cjs migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile19.js',
knexArgs: ['migrate:latest', '--esm'],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
{
title: 'ESM/js knexfile provides mjs migrations',
testCase: 'knexfile-imports',
knexfile: 'knexfile20.js',
knexArgs: ['migrate:latest', '--esm'],
expectedOutput: 'Batch 1 run: 1 migrations',
expectedSchema: [
'knex_migrations',
'sqlite_sequence',
'knex_migrations_lock',
'xyz',
],
dropDb: true,
},
/**
* Seed tests for the above cases
*/
{
title: `Seeds knexfile20.js`,
testCase: 'knexfile-imports',
knexfile: 'knexfile20.js',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile19.js`,
testCase: 'knexfile-imports',
knexfile: 'knexfile19.js',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile18.mjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile18.mjs',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile17.mjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile17.mjs',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile16.mjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile16.mjs',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile15.cjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile15.cjs',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile14.cjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile14.cjs',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile13.cjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile13.cjs',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile12.cjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile12.cjs',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile11.js`,
testCase: 'knexfile-imports',
knexfile: 'knexfile11.js',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile10.js`,
testCase: 'knexfile-imports',
knexfile: 'knexfile10.js',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile9.js`,
testCase: 'knexfile-imports',
knexfile: 'knexfile9.js',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile8.js`,
testCase: 'knexfile-imports',
knexfile: 'knexfile8.js',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile7.js`,
testCase: 'knexfile-imports',
knexfile: 'knexfile7.js',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
title: `Seeds knexfile6.js`,
testCase: 'knexfile-imports',
knexfile: 'knexfile6.js',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
// This case failure on Node10 is already documented
title: `Seeds knexfile5.mjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile5.mjs',
knexArgs: ['seed:run', `--esm`],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
{
// This case failure on Node10 is already documented
title: `Seeds knexfile4.mjs`,
testCase: 'knexfile-imports',
knexfile: 'knexfile4.mjs',
knexArgs: ['seed:run'],
expectedOutput: 'Ran 1 seed files',
dropDb: true,
before: async ({ dbPath }) => {
const db = new sqlite3.Database(dbPath);
await createTable(db, `xyz (name TEXT)`);
},
},
].filter(Boolean);
describe('esm interop and mjs support', () => {
before(() => {
process.env.KNEX_PATH = '../knex.js';
});
for (const spec of fixture) {
it(spec.title, async function () {
// This whole suite got broken in this version
if (semver.gte(process.version, 'v16.6.0')) {
return this.skip();
}
const {
testCase,
knexfile = 'knexfile.js',
dbName = 'test.sqlite3',
nodeArgs = [],
knexArgs,
expectedOutput,
expectedSchema,
expectedErrorMessage,
before,
dropDb = false,
} = spec;
const cwd = path.resolve(path.dirname(KNEX), TEST_BASE, testCase);
const dbPath = path.resolve(cwd, dbName);
// ...
const cmd = [
`node`,
...nodeArgs,
KNEX,
...knexArgs,
`--cwd="${cwd}"`,
`--knexfile=${path.resolve(cwd, knexfile)}`,
`--knexpath=${KNEX}`,
]
.filter(Boolean)
.join(' ');
// ...
if (dropDb && fs.existsSync(dbPath)) fs.unlinkSync(dbPath);
// ...
if (before) {
await before({
...spec,
cmd,
cwd,
testCase,
knexfile,
dbName,
dbPath,
nodeArgs,
knexArgs,
expectedOutput,
expectedSchema,
expectedErrorMessage,
});
}
// ...
await execCommand(cmd, { expectedOutput, expectedErrorMessage });
// ...
if (expectedSchema) {
const db = new sqlite3.Database(dbPath);
const result = await getSchema(db);
await closeDB(db);
// migration performed
assert.deepEqual(
result.map(({ name }) => name),
expectedSchema
);
}
});
}
});
function getSchema(db) {
return new Promise((resolve, reject) => {
db.all('SELECT name from SQLITE_MASTER', (err, rows) => {
if (err) reject(err);
else resolve(rows);
});
});
}
function closeDB(db) {
return new Promise((resolve, reject) =>
db.close((err) => {
if (err) reject(err);
else resolve();
})
);
}