Skip to content

Commit 28a484b

Browse files
committed
fix(test): default --watch to off for ns test in CI environments
--watch defaults to true globally, so `ns test <platform>` always resolved watch mode even when the flag was never passed. For Vitest projects this printed a spurious "'--watch' is not supported" warning on every run, and for Karma projects it kept the process alive in CI instead of exiting after a single run. The test commands now declare their own --watch default, disabled when a CI environment is detected (CI/JENKINS_HOME). Explicit --watch and --no-watch still take precedence, and local behavior is unchanged.
1 parent e157c9f commit 28a484b

7 files changed

Lines changed: 134 additions & 7 deletions

File tree

‎docs/man_pages/project/testing/dev-test-android.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Run tests on a selected device | `$ ns test android --device <Device ID> [--watc
99
Runs the tests in your project on connected Android devices and running native emulators.<% if(isConsole) { %> Your project must already be configured for unit testing by running `$ ns test init`.<% } %>
1010

1111
### Options
12-
* `--watch` - If set, when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-run.
12+
* `--watch` - Enabled by default; when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-run. Pass `--no-watch` to run the tests once. In CI environments (when the `CI` environment variable is set), it defaults to disabled.
1313
* `--device` - Specifies the serial number or the index of the connected device on which to run the tests. To list all connected devices, grouped by platform, run `$ ns device`
1414
* `--debug-brk` - Runs the tests under the debugger. The debugger will break just before your tests are executed, so you have a chance to place breakpoints.
1515

‎docs/man_pages/project/testing/dev-test-ios.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Runs the tests in your project on connected iOS devices or the iOS Simulator.<%
1313

1414
<% if((isConsole && isMacOS) || isHtml) { %>
1515
### Options
16-
* `--watch` - If set, when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-ran.
16+
* `--watch` - Enabled by default; when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-ran. Pass `--no-watch` to run the tests once. In CI environments (when the `CI` environment variable is set), it defaults to disabled.
1717
* `--device` - Specifies the serial number or the index of the connected device on which you want to run tests. To list all connected devices, grouped by platform, run `$ ns device`. You cannot set `--device` and `--emulator` simultaneously.
1818
* `--emulator` - Runs tests on the iOS Simulator. You cannot set `--device` and `--emulator` simultaneously.
1919
* `--debug-brk` - Runs the tests under the debugger. The debugger will break just before your tests are executed, so you have a chance to place breakpoints.

‎docs/man_pages/project/testing/test-android.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Run tests on a selected device | `$ ns test android --device <Device ID> [--watc
1818

1919
### Options
2020

21-
* `--watch` - If set, when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-run.
21+
* `--watch` - Enabled by default; when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-run. Pass `--no-watch` to run the tests once. In CI environments (when the `CI` environment variable is set), it defaults to disabled.
2222
* `--device` - Specifies the serial number or the index of the connected device on which to run the tests. To list all connected devices, grouped by platform, run `$ ns device`. `<Device ID>` is the device index or identifier as listed by the `$ ns device` command.
2323
* `--debug-brk` - Runs the tests under the debugger. The debugger will break just before your tests are executed, so you have a chance to place breakpoints.
2424
* `--env.*` - Specifies additional flags that the bundler may process. Can be passed multiple times. Supported additional flags:

‎docs/man_pages/project/testing/test-ios.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Run tests in the iOS Simulator | `$ ns test ios --emulator [--watch] [--debug-br
2323

2424
### Options
2525

26-
* `--watch` - If set, when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-ran.
26+
* `--watch` - Enabled by default; when you save changes to the project, changes are automatically synchronized to the connected device and tests are re-ran. Pass `--no-watch` to run the tests once. In CI environments (when the `CI` environment variable is set), it defaults to disabled.
2727
* `--device` - Specifies the serial number or the index of the connected device on which you want to run tests. To list all connected devices, grouped by platform, run `$ ns device`. You cannot set `--device` and `--emulator` simultaneously. `<Device ID>` is the device index or identifier as listed by the `$ ns device` command.
2828
* `--emulator` - Runs tests on the iOS Simulator. You cannot set `--device` and `--emulator` simultaneously.
2929
* `--debug-brk` - Runs the tests under the debugger. The debugger will break just before your tests are executed, so you have a chance to place breakpoints.

‎lib/commands/test.ts‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { hasValidAndroidSigning } from "../common/helpers";
1+
import { hasValidAndroidSigning, isCIEnvironment } from "../common/helpers";
22
import {
33
ANDROID_RELEASE_BUILD_ERROR_MESSAGE,
44
ANDROID_APP_BUNDLE_SIGNING_ERROR_MESSAGE,
@@ -25,6 +25,13 @@ abstract class TestCommandBase {
2525
public allowedParameters: ICommandParameter[] = [];
2626
public dashedOptions = {
2727
hmr: { type: OptionType.Boolean, default: false, hasSensitiveValue: false },
28+
// Watch mode keeps the run alive waiting for changes; CI runs must
29+
// execute once and exit, so --watch defaults to off there.
30+
watch: {
31+
type: OptionType.Boolean,
32+
default: !isCIEnvironment(),
33+
hasSensitiveValue: false,
34+
},
2835
};
2936

3037
protected abstract platform: string;
@@ -214,7 +221,7 @@ class TestAndroidCommand extends TestCommandBase implements ICommand {
214221
}
215222
}
216223

217-
class TestIosCommand extends TestCommandBase implements ICommand {
224+
export class TestIosCommand extends TestCommandBase implements ICommand {
218225
protected platform = "iOS";
219226

220227
constructor(

‎lib/common/helpers.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ function isRunningInTTY(): boolean {
349349
);
350350
}
351351

352-
function isCIEnvironment(): boolean {
352+
export function isCIEnvironment(): boolean {
353353
// The following CI environments set their own environment variables that we respect:
354354
// travis: "CI",
355355
// circleCI: "CI",

‎test/commands/test.ts‎

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { Yok } from "../../lib/common/yok";
2+
import { assert } from "chai";
3+
import { Options } from "../../lib/options";
4+
import { TestIosCommand } from "../../lib/commands/test";
5+
import { IOptions } from "../../lib/declarations";
6+
import { ICommand } from "../../lib/common/definitions/commands";
7+
import { IInjector } from "../../lib/common/definitions/yok";
8+
import { IConfigurationSettings } from "../../lib/common/declarations";
9+
10+
const CI_ENVIRONMENT_VARIABLES = ["CI", "JENKINS_HOME"];
11+
12+
function createTestInjector(): IInjector {
13+
const testInjector = new Yok();
14+
testInjector.register("settingsService", {
15+
setSettings: (settings: IConfigurationSettings): any => undefined,
16+
getProfileDir: () => "profileDir",
17+
});
18+
testInjector.register("errors", {
19+
fail: (message: string): never => {
20+
throw new Error(message);
21+
},
22+
failWithHelp: (message: string): never => {
23+
throw new Error(message);
24+
},
25+
});
26+
testInjector.register("logger", {
27+
warn: (message: string): void => undefined,
28+
});
29+
30+
testInjector.register("projectData", {});
31+
testInjector.register("testExecutionService", {});
32+
testInjector.register("vitestExecutionService", {});
33+
testInjector.register("analyticsService", {});
34+
testInjector.register("platformEnvironmentRequirements", {});
35+
testInjector.register("cleanupService", {});
36+
testInjector.register("liveSyncCommandHelper", {});
37+
testInjector.register("devicesService", {});
38+
testInjector.register("migrateController", {});
39+
40+
return testInjector;
41+
}
42+
43+
interface IResolvedTestCommand {
44+
command: ICommand;
45+
options: IOptions;
46+
}
47+
48+
function resolveTestIosCommand(): IResolvedTestCommand {
49+
const testInjector = createTestInjector();
50+
const options = testInjector.resolve(Options);
51+
testInjector.register("options", options);
52+
testInjector.registerCommand("test|ios", TestIosCommand);
53+
const command = testInjector.resolveCommand("test|ios");
54+
return { command, options };
55+
}
56+
57+
function validateWithArgs(
58+
resolved: IResolvedTestCommand,
59+
args: string[] = [],
60+
): void {
61+
args.forEach((arg) => process.argv.push(arg));
62+
resolved.options.validateOptions(resolved.command.dashedOptions);
63+
args.forEach(() => process.argv.pop());
64+
}
65+
66+
describe("test ios command", () => {
67+
const savedCiEnvironment: { [key: string]: string } = {};
68+
69+
beforeEach(() => {
70+
CI_ENVIRONMENT_VARIABLES.forEach((name) => {
71+
savedCiEnvironment[name] = process.env[name];
72+
delete process.env[name];
73+
});
74+
});
75+
76+
afterEach(() => {
77+
CI_ENVIRONMENT_VARIABLES.forEach((name) => {
78+
if (savedCiEnvironment[name] === undefined) {
79+
delete process.env[name];
80+
} else {
81+
process.env[name] = savedCiEnvironment[name];
82+
}
83+
});
84+
});
85+
86+
describe("--watch option", () => {
87+
it("defaults to off in CI environments", () => {
88+
process.env.CI = "true";
89+
const resolved = resolveTestIosCommand();
90+
validateWithArgs(resolved);
91+
assert.isFalse(resolved.options.argv.watch);
92+
});
93+
94+
it("defaults to off when JENKINS_HOME is set", () => {
95+
process.env.JENKINS_HOME = "/var/jenkins";
96+
const resolved = resolveTestIosCommand();
97+
validateWithArgs(resolved);
98+
assert.isFalse(resolved.options.argv.watch);
99+
});
100+
101+
it("defaults to on outside CI environments", () => {
102+
const resolved = resolveTestIosCommand();
103+
validateWithArgs(resolved);
104+
assert.isTrue(resolved.options.argv.watch);
105+
});
106+
107+
it("stays on when --watch is passed explicitly in CI", () => {
108+
process.env.CI = "true";
109+
const resolved = resolveTestIosCommand();
110+
validateWithArgs(resolved, ["--watch"]);
111+
assert.isTrue(resolved.options.argv.watch);
112+
});
113+
114+
it("stays off when --no-watch is passed outside CI", () => {
115+
const resolved = resolveTestIosCommand();
116+
validateWithArgs(resolved, ["--no-watch"]);
117+
assert.isFalse(resolved.options.argv.watch);
118+
});
119+
});
120+
});

0 commit comments

Comments
 (0)