Skip to content

Commit

Permalink
refactor: add tests and replace minimatch with picomatch (#248)
Browse files Browse the repository at this point in the history
* test: add expo cli invocation tests

* test: add expo manifest tests

* test: add expo manifest plugin tests

* test: add file path tests

* refactor: replace `minimatch` with `picomatch`

* refactor: mark `@expo/config-plugins` as test dependency

* fix: do not return `null` for `./` as file directory
  • Loading branch information
byCedric authored Mar 7, 2024
1 parent c05fe1d commit 519d179
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 23 deletions.
87 changes: 72 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,23 @@
"debug": "^4.3.4",
"find-up": "^5.0.0",
"jsonc-parser": "^3.0.0",
"minimatch": "^3.0.4",
"node-fetch": "^2.6.7"
"node-fetch": "^2.6.7",
"picomatch": "^4.0.1"
},
"devDependencies": {
"@expo/config-plugins": "^4.1.5",
"@expo/spawn-async": "^1.7.2",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@sucrase/webpack-loader": "^2.0.0",
"@tsconfig/node18": "^18.2.1",
"@types/chai": "^4.3.5",
"@types/chai-subset": "^1.3.3",
"@types/debug": "^4.1.7",
"@types/minimatch": "^3.0.5",
"@types/mocha": "^10.0.1",
"@types/node": "^18.17.12",
"@types/node-fetch": "^2.6.2",
"@types/picomatch": "^2.3.3",
"@types/sinon": "^10.0.16",
"@types/sinon-chai": "^3.2.12",
"@types/vscode": "^1.78.2",
Expand All @@ -81,6 +83,7 @@
"node-fetch": "^2.6.7",
"ovsx": "^0.8.3",
"patch-package": "^6.4.7",
"picomatch": "^4.0.1",
"prettier": "^3.0.3",
"raw-loader": "^4.0.2",
"semantic-release": "^21.1.1",
Expand Down
19 changes: 19 additions & 0 deletions src/__tests__/utils/spawn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { stub, type SinonStub } from 'sinon';

import * as spawn from '../../utils/spawn';

/** Mock spawn with a default empty device list response */
export function stubSpawn(result?: Partial<spawn.SpawnResult>) {
const spawnStub = withSpawnResult(stub(spawn, 'spawn'), result);
// @ts-expect-error
spawnStub[Symbol.dispose] = () => spawnStub.restore();
return spawnStub as Disposable & typeof spawnStub;
}

export function withSpawnResult<T extends SinonStub>(
spawnStub: T,
result: Partial<spawn.SpawnResult> = {}
) {
spawnStub.returns(Promise.resolve(result));
return spawnStub;
}
49 changes: 49 additions & 0 deletions src/expo/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { expect } from 'chai';
import { match } from 'sinon';

import { stubSpawn } from '../../__tests__/utils/spawn';
import { spawnExpoCli } from '../cli';

describe('spawnExpoCli', () => {
it('executes spawn with `npx expo` command', async () => {
using spawn = stubSpawn();

await spawnExpoCli('whoami', ['--json'], { stdio: 'inherit' });

expect(spawn).to.be.calledWith(
'npx',
match(['expo', 'whoami', '--json']),
match({ stdio: 'inherit' })
);
});

it('returns the output of spawned process', async () => {
using _spawn = stubSpawn({ stdout: 'testuser' });

expect(await spawnExpoCli('whoami')).to.equal('testuser');
});

it('forces expo in non-interactive mode', async () => {
using spawn = stubSpawn();

await spawnExpoCli('whoami');

expect(spawn).to.be.calledWith(
'npx',
match(['expo', 'whoami']),
match({ env: match({ CI: 'true' }) })
);
});

it('forces expo without telemetry', async () => {
using spawn = stubSpawn();

await spawnExpoCli('whoami');

expect(spawn).to.be.calledWith(
'npx',
match(['expo', 'whoami']),
match({ env: match({ EXPO_NO_TELEMETRY: 'true' }) })
);
});
});
52 changes: 52 additions & 0 deletions src/expo/__tests__/manifest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { expect } from 'chai';
import picomatch from 'picomatch';

import { type ExpoConfig } from '../../packages/config';
import { manifestPattern, getFileReferences } from '../manifest';

describe('manifestPattern', () => {
it('scheme is set to files', () => {
expect(manifestPattern.scheme).to.equal('file');
});

it('language is set to json with comments', () => {
expect(manifestPattern.language).to.equal('jsonc');
});

it('pattern includes all json variations of the Expo manifest', () => {
const pattern = manifestPattern.pattern as string;
const matcher = picomatch(pattern);

expect(matcher('my-app/app.json')).to.be.true;
expect(matcher('my-app/app.config.json')).to.be.true;
});
});

describe('getFileReferences', () => {
it('returns all local file references from manifest', () => {
const manifest: ExpoConfig = {
name: 'my-app',
slug: 'my-app',
icon: './assets/icon.png',
splash: {
image: '../assets/splash.png',
backgroundColor: '#FFFFFF',
resizeMode: 'cover',
},
android: {
adaptiveIcon: {
foregroundImage: './assets/adaptive-icon.png',
backgroundColor: '#FFFFFF',
},
},
plugins: ['./plugins/local-plugin.js'],
};

expect(getFileReferences(JSON.stringify({ expo: manifest }, null, 2))).to.deep.include.members([
{ filePath: './assets/icon.png', fileRange: { length: 17, offset: 71 } },
{ filePath: '../assets/splash.png', fileRange: { length: 20, offset: 123 } },
{ filePath: './assets/adaptive-icon.png', fileRange: { length: 26, offset: 286 } },
{ filePath: './plugins/local-plugin.js', fileRange: { length: 25, offset: 391 } },
]);
});
});
Loading

0 comments on commit 519d179

Please sign in to comment.