Skip to content
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

test: add --ordering unit tests with chai #357

Merged
merged 3 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
test:
name: Test
strategy:
fail-fast: false
matrix:
node-version:
- '20.10'
Expand Down
9 changes: 9 additions & 0 deletions .mocharc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
"watch-files": [
"test/**/*.js",
"lib/**/*.js"
],
"recursive": true,
"file": "./mocha.setup.js", // setup file before everything else loads
"forbid-only": true
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add root-level ./*.js scripts to prettier:write as well

4 changes: 4 additions & 0 deletions mocha.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
const chai = require("chai");
const { jestSnapshotPlugin } = require("mocha-chai-jest-snapshot");

chai.use(jestSnapshotPlugin());
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"scripts": {
"build": "tsc",
"mocha": "xvfb-maybe electron-mocha --reporter spec && mocha --reporter spec",
"mocha:watch": "mocha --watch",
"test": "yarn lint && yarn mocha",
"lint": "yarn prettier:check",
"prettier": "prettier \"src/**/*.ts\" \"test/**/*.js\"",
Expand All @@ -44,10 +45,12 @@
"devDependencies": {
"@types/minimatch": "^3.0.5",
"@types/node": "^12.0.0",
"chai": "^4",
"electron": "^22.0.0",
"electron-mocha": "^13.0.1",
"lodash": "^4.17.15",
"mocha": "^10.1.0",
"mocha-chai-jest-snapshot": "^1.1.6",
"prettier": "^3.3.3",
"rimraf": "^3.0.2",
"typescript": "^5.5.4",
Expand Down
165 changes: 165 additions & 0 deletions test/__snapshots__/cli-spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`command line interface should respect ordering file (format: "\${filepath}") 1`] = `
Object {
"files": Object {
"private": Object {
"files": Object {
"var": Object {
"files": Object {
"app": Object {
"files": Object {
"file.txt": Object {
"link": "private/var/file.txt",
},
},
},
"file.txt": Object {
"integrity": Object {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": Array [
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
],
"hash": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
},
"size": 11,
},
},
},
},
},
"var": Object {
"link": "private/var",
},
},
}
`;

exports[`command line interface should respect ordering file (format: "\${random number} : \${filepath}") 1`] = `
Object {
"files": Object {
"private": Object {
"files": Object {
"var": Object {
"files": Object {
"app": Object {
"files": Object {
"file.txt": Object {
"link": "private/var/file.txt",
},
},
},
"file.txt": Object {
"integrity": Object {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": Array [
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
],
"hash": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
},
"size": 11,
},
},
},
},
},
"var": Object {
"link": "private/var",
},
},
}
`;

exports[`command line interface should respect ordering file (format: ": \${filepath}") 1`] = `
Object {
"files": Object {
"private": Object {
"files": Object {
"var": Object {
"files": Object {
"app": Object {
"files": Object {
"file.txt": Object {
"link": "private/var/file.txt",
},
},
},
"file.txt": Object {
"integrity": Object {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": Array [
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
],
"hash": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
},
"size": 11,
},
},
},
},
},
"var": Object {
"link": "private/var",
},
},
}
`;

exports[`command line interface should unpack static framework with all underlying symlinks unpacked 1`] = `
Object {
"files": Object {
"private": Object {
"files": Object {
"var": Object {
"files": Object {
"app": Object {
"files": Object {
"file.txt": Object {
"link": "private/var/file.txt",
"unpacked": true,
},
},
},
"file.txt": Object {
"integrity": Object {
"algorithm": "SHA256",
"blockSize": 4194304,
"blocks": Array [
"b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
],
"hash": "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
},
"size": 11,
"unpacked": true,
},
},
},
},
},
"var": Object {
"link": "private/var",
"unpacked": true,
},
},
}
`;

exports[`command line interface should unpack static framework with all underlying symlinks unpacked 2`] = `
Array [
Object {
"content": "hello world",
"name": "private/var/app/file.txt",
},
"private/var/app",
Object {
"content": "hello world",
"name": "private/var/file.txt",
},
"private/var",
"private",
"var",
]
`;
3 changes: 2 additions & 1 deletion test/api-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const compDirs = require('./util/compareDirectories');
const compFileLists = require('./util/compareFileLists');
const { compFiles, isSymbolicLinkSync } = require('./util/compareFiles');
const transform = require('./util/transformStream');
const { TEST_APPS_DIR } = require('./util/constants');

async function assertPackageListEquals(actualList, expectedFilename) {
const expected = await fs.readFile(expectedFilename, 'utf8');
Expand All @@ -19,7 +20,7 @@ async function assertPackageListEquals(actualList, expectedFilename) {

describe('api', function () {
beforeEach(() => {
rimraf.sync(path.join(__dirname, '..', 'tmp'), fs);
rimraf.sync(TEST_APPS_DIR, fs);
});

it('should create archive from directory', async () => {
Expand Down
58 changes: 46 additions & 12 deletions test/cli-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const compDirs = require('./util/compareDirectories');
const compFileLists = require('./util/compareFileLists');
const { compFiles } = require('./util/compareFiles');
const createSymlinkApp = require('./util/createSymlinkApp');
const { verifySmartUnpack } = require('./util/verifySmartUnpack');
const { TEST_APPS_DIR } = require('./util/constants');

const exec = promisify(childProcess.exec);

Expand All @@ -29,7 +31,7 @@ async function assertAsarOutputMatches(args, expectedFilename) {

describe('command line interface', function () {
beforeEach(() => {
rimraf.sync(path.join(__dirname, '..', 'tmp'), fs);
rimraf.sync(TEST_APPS_DIR, fs);
});

it('should create archive from directory', async () => {
Expand Down Expand Up @@ -190,21 +192,53 @@ describe('command line interface', function () {
);
});
it('should unpack static framework with all underlying symlinks unpacked', async () => {
const { tmpPath } = createSymlinkApp('app');
const { testPath } = await createSymlinkApp('app');
await execAsar(
`p ${tmpPath} tmp/packthis-with-symlink.asar --unpack *.txt --unpack-dir var --exclude-hidden`,
`p ${testPath} tmp/packthis-with-symlink.asar --unpack *.txt --unpack-dir var --exclude-hidden`,
);

assert.ok(fs.existsSync('tmp/packthis-with-symlink.asar.unpacked/private/var/file.txt'));
assert.ok(fs.existsSync('tmp/packthis-with-symlink.asar.unpacked/private/var/app/file.txt'));
assert.strictEqual(
fs.readlinkSync('tmp/packthis-with-symlink.asar.unpacked/private/var/app/file.txt'),
path.join('..', 'file.txt'),
await verifySmartUnpack('tmp/packthis-with-symlink.asar');
});
it('should respect ordering file (format: "${filepath}")', async () => {
const { testPath, filesOrdering } = await createSymlinkApp('app');

const orderingPath = path.join(testPath, '../ordered-app-ordering1.txt');
const data = filesOrdering.reduce((prev, curr) => {
return `${prev}${curr}\n`;
}, '');
await fs.writeFile(orderingPath, data);

await execAsar(
`p ${testPath} tmp/packthis-with-symlink.asar --ordering=${orderingPath} --exclude-hidden`,
);
assert.strictEqual(
fs.readlinkSync('tmp/packthis-with-symlink.asar.unpacked/var'),
path.join('private', 'var'),
await verifySmartUnpack('tmp/packthis-with-symlink.asar');
});
it('should respect ordering file (format: ": ${filepath}")', async () => {
const { testPath, filesOrdering } = await createSymlinkApp('app');

const orderingPath = path.join(testPath, '../ordered-app-ordering2.txt');
const data = filesOrdering.reduce((prev, curr) => {
return `${prev}: ${curr}\n`;
}, '');
await fs.writeFile(orderingPath, data);

await execAsar(
`p ${testPath} tmp/packthis-with-symlink.asar --ordering=${orderingPath} --exclude-hidden`,
);
await verifySmartUnpack('tmp/packthis-with-symlink.asar');
});
it('should respect ordering file (format: "${random number} : ${filepath}")', async () => {
const { testPath, filesOrdering } = await createSymlinkApp('app');

const orderingPath = path.join(testPath, '../ordered-app-ordering3.txt');
const data = filesOrdering.reduce((prev, curr) => {
return `${prev}${Math.floor(Math.random() * 1000)} : ${curr} \n`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: what happens if the random number has a collision?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing, current logic ignores anything before the :

asar/src/asar.ts

Lines 93 to 95 in 9b7ccfe

if (line.includes(':')) {
line = line.split(':').pop()!;
}

}, '');
await fs.writeFile(orderingPath, data);

await execAsar(
`p ${testPath} tmp/packthis-with-symlink.asar --ordering=${orderingPath} --exclude-hidden`,
);
assert.ok(fs.existsSync('tmp/packthis-with-symlink.asar.unpacked/var/file.txt'));
await verifySmartUnpack('tmp/packthis-with-symlink.asar');
});
});
5 changes: 3 additions & 2 deletions test/filesystem-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ const fs = require('../lib/wrapped-fs').default;
const path = require('path');
const rimraf = require('rimraf');
const createSymlinkedApp = require('./util/createSymlinkApp');
const { TEST_APPS_DIR } = require('./util/constants');

const Filesystem = require('../lib/filesystem').Filesystem;

describe('filesystem', function () {
beforeEach(() => {
rimraf.sync(path.join(__dirname, '..', 'tmp'), fs);
rimraf.sync(TEST_APPS_DIR, fs);
});

it('should does not throw an error when the src path includes a symbol link', async () => {
const { appPath, varPath } = createSymlinkedApp('filesystem');
const { appPath, varPath } = await createSymlinkedApp('filesystem');
const filesystem = new Filesystem(varPath);
assert.doesNotThrow(() => {
filesystem.insertLink(path.join(appPath, 'file.txt'));
Expand Down
1 change: 1 addition & 0 deletions test/util/compareDirectories.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = async function (dirA, dirB) {
differentFiles.push(filename);
continue;
}
// TODO: FIXME - This promise needs an `await`, but the fixtures are flaky across OS's and it should be investigated separately
const [fileContentA, fileContentB] = Promise.all(
[dirA, dirB].map((dir) => fs.readFile(path.join(dir, filename), 'utf8')),
);
Expand Down
9 changes: 9 additions & 0 deletions test/util/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const path = require('path');

// root dir of the repo
const ROOT_PROJECT_DIR = path.resolve(__dirname, '..', '..');

// tmp dir we use for test artifacts
const TEST_APPS_DIR = path.join(ROOT_PROJECT_DIR, 'tmp');

module.exports = { ROOT_PROJECT_DIR, TEST_APPS_DIR };
Loading