Skip to content

Commit a52a480

Browse files
committed
feat: unit testing executor and configuration
1 parent 506d106 commit a52a480

File tree

6 files changed

+239
-1
lines changed

6 files changed

+239
-1
lines changed

packages/nx/README.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- [Develop on simulators and devices](#develop-on-simulators-and-devices)
2626
- [Configuration options](#configuration-options)
2727
- [Run with a specific configuration](#run-with-a-specific-configuration)
28+
- [Run tests](#run-tests)
2829
- [Create a build](#create-a-build)
2930
- [Clean](#clean)
3031
- [Create NativeScript library](#create-nativescript-library)
@@ -192,7 +193,7 @@ The options follow the [NativeScript command line option flags](https://docs.nat
192193

193194
Here's an example app config:
194195

195-
```
196+
```json
196197
"nativescript-mobile": {
197198
"projectType": "application",
198199
"root": "apps/nativescript-mobile/",
@@ -253,6 +254,17 @@ Here's an example app config:
253254
}
254255
}
255256
},
257+
"test": {
258+
"executor": "@nativescript/nx:test",
259+
"outputs": ["coverage/apps/nativescript-mobile"],
260+
"options": {
261+
"coverage": false
262+
},
263+
"configurations": {
264+
"android": {},
265+
"ios": {}
266+
}
267+
},
256268
"clean": {
257269
"builder": "@nativescript/nx:build",
258270
"options": {
@@ -277,6 +289,46 @@ npx nx run <app-name>:android:prod
277289
npx nx run <app-name>:ios:prod
278290
```
279291

292+
#### Run tests
293+
294+
**Android:**
295+
296+
```sh
297+
npx nx run <app-name>:test:android
298+
```
299+
300+
**iOS:** (Mac only)
301+
302+
```sh
303+
npx nx run <app-name>:test:ios
304+
```
305+
306+
You can generate coverage reports by using the flag with iOS or Android, for example:
307+
308+
```sh
309+
npx nx run <app-name>:test:ios --coverage
310+
```
311+
312+
You can also set this option in the config, for example:
313+
314+
```json
315+
"test": {
316+
"executor": "@nativescript/nx:test",
317+
"outputs": ["coverage/apps/nativescript-mobile"],
318+
"options": {
319+
"coverage": true // can set to always be on for both platforms
320+
},
321+
"configurations": {
322+
"android": {
323+
"coverage": false // or can override per platform if needed
324+
},
325+
"ios": {
326+
"coverage": true
327+
}
328+
}
329+
}
330+
```
331+
280332
#### Create a build
281333

282334
Instead of running the app on a simulator or device you can create a build for the purposes of distribution/release. Various release settings will be needed for iOS and Android which can be passed as additional command line arguments. [See more in the NativeScript docs here](https://docs.nativescript.org/releasing.html#overview). Any additional cli flags as stated in the docs can be passed on the end of the `nx build` command that follows.

packages/nx/builders.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
"implementation": "./src/builders/build/builder",
66
"schema": "./src/builders/build/schema.json",
77
"description": "NativeScript builder"
8+
},
9+
"test": {
10+
"implementation": "./src/builders/test/builder",
11+
"schema": "./src/builders/test/schema.json",
12+
"description": "Start the NativeScript unit test runner"
813
}
914
}
1015
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Architect } from '@angular-devkit/architect';
2+
import { TestingArchitectHost } from '@angular-devkit/architect/testing';
3+
import { schema } from '@angular-devkit/core';
4+
import { join } from 'path';
5+
import { TestBuilderSchema } from './schema';
6+
import runBuilder from './builder';
7+
8+
const options: TestBuilderSchema = {
9+
codeCoverage: false,
10+
platform: 'ios',
11+
};
12+
13+
xdescribe('NativeScript Test Builder', () => {
14+
const context = {
15+
logger: {
16+
info: (args) => {
17+
console.log(args);
18+
},
19+
},
20+
} as any;
21+
let architect: Architect;
22+
let architectHost: TestingArchitectHost;
23+
24+
beforeEach(async () => {
25+
const registry = new schema.CoreSchemaRegistry();
26+
registry.addPostTransform(schema.transforms.addUndefinedDefaults);
27+
28+
architectHost = new TestingArchitectHost('/root', '/root');
29+
architect = new Architect(architectHost, registry);
30+
31+
// This will either take a Node package name, or a path to the directory
32+
// for the package.json file.
33+
await architectHost.addBuilderFromPackage(join(__dirname, '../../..'));
34+
});
35+
});
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { ExecutorContext, convertNxExecutor } from '@nrwl/devkit';
2+
import * as childProcess from 'child_process';
3+
import { TestBuilderSchema } from './schema';
4+
5+
export function runBuilder(options: TestBuilderSchema, context: ExecutorContext): Promise<{ success: boolean }> {
6+
return new Promise((resolve, reject) => {
7+
const projectConfig = context.workspace.projects[context.projectName];
8+
// console.log('context.projectName:', context.projectName);
9+
const projectCwd = projectConfig.root;
10+
// console.log('projectCwd:', projectCwd);
11+
// console.log('context.targetName:', context.targetName);
12+
// console.log('context.configurationName:', context.configurationName);
13+
// console.log('context.target.options:', context.target.options);
14+
15+
let targetConfigName = '';
16+
if (context.configurationName && context.configurationName !== 'build') {
17+
targetConfigName = context.configurationName;
18+
}
19+
20+
// determine if any trailing args that need to be added to run/build command
21+
const configTarget = targetConfigName ? `:${targetConfigName}` : '';
22+
const projectTargetCmd = `${context.projectName}:${context.targetName}${configTarget}`;
23+
const projectTargetCmdIndex = process.argv.findIndex((c) => c === projectTargetCmd);
24+
// const additionalCliFlagArgs = [];
25+
// if (process.argv.length > projectTargetCmdIndex+1) {
26+
// additionalCliFlagArgs.push(...process.argv.slice(projectTargetCmdIndex+1, process.argv.length));
27+
// // console.log('additionalCliFlagArgs:', additionalCliFlagArgs);
28+
// }
29+
30+
const throwPlatformError = () => {
31+
throw new Error(`Configuration must exist for 'ios' or 'android' or options.platform should be set for this target.`);
32+
}
33+
34+
const nsOptions = ['test'];
35+
36+
const fileReplacements: Array<string> = [];
37+
let configOptions;
38+
if (context.target.configurations) {
39+
configOptions = context.target.configurations[targetConfigName];
40+
// console.log('configOptions:', configOptions)
41+
42+
if (configOptions) {
43+
if (['ios', 'android'].includes(targetConfigName)) {
44+
nsOptions.push(targetConfigName);
45+
} else if (options.platform) {
46+
nsOptions.push(options.platform);
47+
} else {
48+
throwPlatformError();
49+
}
50+
if (configOptions.coverage) {
51+
nsOptions.push('--env.codeCoverage');
52+
}
53+
if (configOptions.fileReplacements) {
54+
for (const r of configOptions.fileReplacements) {
55+
fileReplacements.push(`${r.replace.replace(projectCwd, './')}:${r.with.replace(projectCwd, './')}`);
56+
}
57+
}
58+
}
59+
}
60+
61+
const hasPlatform = nsOptions.filter(o => ['ios', 'android'].includes(o)).length > 0;
62+
if (!hasPlatform) {
63+
throwPlatformError();
64+
}
65+
66+
if (options.coverage && !nsOptions.includes('--env.codeCoverage')) {
67+
// allow target override for all configurations
68+
nsOptions.push('--env.codeCoverage');
69+
}
70+
71+
if (options.device) {
72+
nsOptions.push('--device');
73+
nsOptions.push(options.device);
74+
}
75+
76+
if (fileReplacements.length) {
77+
// console.log('fileReplacements:', fileReplacements);
78+
nsOptions.push('--env.replace');
79+
nsOptions.push(fileReplacements.join(','));
80+
}
81+
// always add --force (unless explicity set to false) for now since within Nx we use @nativescript/webpack at root only and the {N} cli shows a blocking error if not within the app
82+
if (options?.force !== false) {
83+
nsOptions.push('--force');
84+
}
85+
86+
// additional args after -- should be passed through
87+
const argSeparator = process.argv.findIndex((arg) => arg === '--');
88+
let additionalArgs = [];
89+
if (argSeparator >= 0) {
90+
additionalArgs = process.argv.slice(argSeparator + 1);
91+
}
92+
93+
console.log('---');
94+
console.log(`Running NativeScript unit tests within ${projectCwd}`);
95+
console.log(' ');
96+
console.log([`ns`, ...nsOptions, ...additionalArgs].join(' '));
97+
console.log('---');
98+
// console.log('command:', [`ns`, ...nsOptions].join(' '));
99+
const child = childProcess.spawn(/^win/.test(process.platform) ? 'ns.cmd' : 'ns', [...nsOptions, ...additionalArgs], {
100+
cwd: projectCwd,
101+
stdio: 'inherit',
102+
});
103+
child.on('close', (code) => {
104+
console.log(`Done.`);
105+
resolve({ success: code === 0 });
106+
});
107+
});
108+
}
109+
110+
export default convertNxExecutor(runBuilder);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { JsonObject } from '@angular-devkit/core';
2+
3+
export interface TestBuilderSchema extends JsonObject {
4+
platform?: 'ios' | 'android';
5+
coverage?: boolean;
6+
device?: string;
7+
force?: boolean;
8+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"$schema": "http://json-schema.org/schema",
3+
"title": "Start the NativeScript unit test runner",
4+
"description": "",
5+
"type": "object",
6+
"properties": {
7+
"platform": {
8+
"type": "string",
9+
"description": "Platform to test."
10+
},
11+
"coverage": {
12+
"type": "boolean",
13+
"default": false,
14+
"description": "Enable code coverage reports."
15+
},
16+
"device": {
17+
"type": "string",
18+
"description": "Device identifier to run tests on.",
19+
"alias": "d"
20+
},
21+
"force": {
22+
"type": "boolean",
23+
"default": true,
24+
"description": "If true, skips the application compatibility checks and forces npm i to ensure all dependencies are installed. Otherwise, the command will check the application compatibility with the current CLI version and could fail requiring ns migrate."
25+
}
26+
},
27+
"required": []
28+
}

0 commit comments

Comments
 (0)