@ohos-ports/jest-dev-server 0.0.0-stage → 11.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2024 Smooth Code
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,227 @@
1
- # Temporary Holding Version
1
+ # jest-dev-server
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/jest-dev-server.svg)](https://www.npmjs.com/package/jest-dev-server)
4
+ [![npm dm](https://img.shields.io/npm/dm/jest-dev-server.svg)](https://www.npmjs.com/package/jest-dev-server)
5
+ [![npm dt](https://img.shields.io/npm/dt/jest-dev-server.svg)](https://www.npmjs.com/package/jest-dev-server)
6
+
7
+ Starts a server before your Jest tests and tears it down after.
8
+
9
+ ## Why
10
+
11
+ `jest-puppeteer` works great for running tests in Jest using Puppeteer.
12
+ It's also useful for starting a local development server during the tests without letting Jest hang.
13
+ This package extracts just the local development server spawning without any ties to Puppeteer.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install --save-dev jest-dev-server
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ `jest-dev-server` exports `setup` and `teardown` functions.
24
+
25
+ ```js
26
+ // global-setup.js
27
+ const { setup: setupDevServer } = require("jest-dev-server");
28
+
29
+ module.exports = async function globalSetup() {
30
+ globalThis.servers = await setupDevServer({
31
+ command: `node config/start.js --port=3000`,
32
+ launchTimeout: 50000,
33
+ port: 3000,
34
+ });
35
+ // Your global setup
36
+ };
37
+ ```
38
+
39
+ ```js
40
+ // global-teardown.js
41
+ const { teardown: teardownDevServer } = require("jest-dev-server");
42
+
43
+ module.exports = async function globalTeardown() {
44
+ await teardownDevServer(globalThis.servers);
45
+ // Your global teardown
46
+ };
47
+ ```
48
+
49
+ ### Specify several servers
50
+
51
+ You can specify several servers using an array of configs:
52
+
53
+ ```js
54
+ // global-setup.js
55
+ const { setup: setupDevServer } = require("jest-dev-server");
56
+
57
+ module.exports = async function globalSetup() {
58
+ globalThis.servers = await setupDevServer([
59
+ {
60
+ command: "node server.js",
61
+ port: 4444,
62
+ },
63
+ {
64
+ command: "node server2.js",
65
+ port: 4445,
66
+ },
67
+ ]);
68
+ // Your global setup
69
+ };
70
+ ```
71
+
72
+ ## Options
73
+
74
+ ### `command`
75
+
76
+ Type: `string`, required.
77
+
78
+ Command to execute to start the port.
79
+ Directly passed to [`spawnd`](https://www.npmjs.com/package/spawnd).
80
+
81
+ ```js
82
+ const options = {
83
+ command: "npm run start",
84
+ };
85
+ ```
86
+
87
+ ### `debug`
88
+
89
+ Type: `boolean`, default to `false`.
90
+
91
+ Log server output, useful if server is crashing at start.
92
+
93
+ ```js
94
+ const options = {
95
+ command: "npm run start",
96
+ debug: true,
97
+ };
98
+ ```
99
+
100
+ ### `launchTimeout`
101
+
102
+ Type: `number`, default to `5000`.
103
+
104
+ How many milliseconds to wait for the spawned server to be available before giving up.
105
+ Defaults to [`wait-port`](https://www.npmjs.com/package/wait-port)'s default.
106
+
107
+ ```js
108
+ const options = {
109
+ command: "npm run start",
110
+ launchTimeout: 30000,
111
+ };
112
+ ```
113
+
114
+ ---
115
+
116
+ Following options are linked to [`spawnd`](https://www.npmjs.com/package/spawnd).
117
+
118
+ ### `host`
119
+
120
+ Type: `string`, if not specified it will used Node.js default port.
121
+
122
+ Host to wait for activity on before considering the server running.
123
+ Must be used in conjunction with `port`.
124
+
125
+ ```js
126
+ const options = {
127
+ command: "npm run start --port 3000",
128
+ host: "customhost.com",
129
+ port: 3000,
130
+ };
131
+ ```
132
+
133
+ ### `path`
134
+
135
+ Type: `string`, default to `null`.
136
+
137
+ Path to resource to wait for activity on before considering the server running.
138
+ Must be used in conjunction with `host` and `port`.
139
+
140
+ ```js
141
+ const options = {
142
+ command: "npm run start --port 3000",
143
+ host: "customhost.com",
144
+ port: 3000,
145
+ path: "thing",
146
+ };
147
+ ```
148
+
149
+ ### `protocol`
150
+
151
+ Type: `string`, (`https`, `http`, `tcp`, `socket`) default to `tcp`.
152
+
153
+ To wait for an HTTP or TCP endpoint before considering the server running, include `http` or `tcp` as a protocol.
154
+ Must be used in conjunction with `port`.
155
+
156
+ ```js
157
+ const options = {
158
+ command: "npm run start --port 3000",
159
+ protocol: "http",
160
+ port: 3000,
161
+ };
162
+ ```
163
+
164
+ ### `port`
165
+
166
+ Type: `number`, default to `null`.
167
+
168
+ Port to wait for activity on before considering the server running.
169
+ If not provided, the server is assumed to immediately be running.
170
+
171
+ ```js
172
+ const options = {
173
+ command: "npm run start --port 3000",
174
+ port: 3000,
175
+ };
176
+ ```
177
+
178
+ ### `usedPortAction`
179
+
180
+ Type: `string` (`ask`, `error`, `ignore`, `kill`) default to `ask`.
181
+
182
+ It defines the action to take if port is already used:
183
+
184
+ - `ask`: a prompt is shown to decide if you want to kill the process or not
185
+ - `error`: an errow is thrown
186
+ - `ignore`: your test are executed, we assume that the server is already started
187
+ - `kill`: the process is automatically killed without a prompt
188
+
189
+ ```js
190
+ const options = {
191
+ command: "npm run start --port 3000",
192
+ port: 3000,
193
+ usedPortAction: "kill",
194
+ };
195
+ ```
196
+
197
+ ### `waitOnScheme`
198
+
199
+ `jest-dev-server` use the [`wait-on`](https://www.npmjs.com/package/wait-on) npm package to wait for resources to become available before calling callback.
200
+
201
+ Type: `object`, default to `{}`.
202
+
203
+ - `delay`: optional initial delay in ms, default 0
204
+ - `interval`: optional poll resource interval in ms, default 250ms
205
+ - `log`: optional flag which outputs to stdout, remaining resources waited on and when complete or errored
206
+ - `reverse`: optional flag to reverse operation so checks are for resources being NOT available, default false
207
+ - `timeout`: optional timeout in ms, default Infinity. Aborts with error
208
+ - `tcpTimeout`: optional tcp timeout in ms, default 300ms
209
+ - `verbose`: optional flag which outputs debug output, default false
210
+ - `window`: optional stabilization time in ms, default 750ms. Waits this amount of time for file sizes to stabilize or other resource availability to remain unchanged
211
+
212
+ **Note:** http(s) specific options, see https://github.com/request/request#readme for specific details
213
+
214
+ ```js
215
+ const options = {
216
+ command: "npm run start --port 3000",
217
+ port: 3000,
218
+ usedPortAction: "kill",
219
+ waitOnScheme: {
220
+ delay: 1000,
221
+ },
222
+ };
223
+ ```
224
+
225
+ ## Troubleshooting
226
+
227
+ - If using `port` makes the terminal to ask for root password although the port is valid and accessible then use `usePortAction: 'ignore'`.
@@ -0,0 +1,70 @@
1
+ import { SpawndOptions, SpawndChildProcess } from 'spawnd';
2
+ import { WaitOnOptions } from 'wait-on';
3
+
4
+ type Config = {
5
+ /**
6
+ * Command to execute to start the port.
7
+ * Directly passed to [`spawnd`](https://www.npmjs.com/package/spawnd).
8
+ * @example "npm run start"
9
+ * @example "yarn start"
10
+ * @example "node server.js"
11
+ * @see https://www.npmjs.com/package/spawnd#command
12
+ */
13
+ command: string;
14
+ /**
15
+ * Enable debug mode.
16
+ * @default false
17
+ */
18
+ debug?: boolean;
19
+ /**
20
+ * Spawnd options.
21
+ * @see https://www.npmjs.com/package/spawnd#options
22
+ */
23
+ options?: SpawndOptions;
24
+ /**
25
+ * Timeout to wait for the server to start.
26
+ * @default 5000
27
+ */
28
+ launchTimeout?: number;
29
+ /**
30
+ * Host to use to check if the port is used.
31
+ */
32
+ host?: string;
33
+ /**
34
+ * Port to use to check if the port is used.
35
+ */
36
+ port?: number;
37
+ /**
38
+ * Path to use to check if the port is used.
39
+ */
40
+ path?: string;
41
+ /**
42
+ * Protocol to use to check if the port is used.
43
+ * @default "tcp"
44
+ */
45
+ protocol?: "tcp" | "http" | "https" | "socket";
46
+ /**
47
+ * Action to take if the port is already used.
48
+ * @default "ask"
49
+ */
50
+ usedPortAction?: "ask" | "error" | "ignore" | "kill";
51
+ /**
52
+ * Options to pass to [`wait-on`](https://www.npmjs.com/package/wait-on).
53
+ * @see https://www.npmjs.com/package/wait-on#options
54
+ */
55
+ waitOnScheme?: WaitOnOptions;
56
+ };
57
+ declare const ERROR_TIMEOUT = "ERROR_TIMEOUT";
58
+ declare const ERROR_PORT_USED = "ERROR_PORT_USED";
59
+ declare const ERROR_NO_COMMAND = "ERROR_NO_COMMAND";
60
+ declare class JestDevServerError extends Error {
61
+ code?: string;
62
+ constructor(message: string, options?: {
63
+ code?: string;
64
+ cause?: Error;
65
+ });
66
+ }
67
+ declare function setup(providedConfigs: Config | Config[]): Promise<SpawndChildProcess[]>;
68
+ declare function teardown(procs: SpawndChildProcess[]): Promise<void>;
69
+
70
+ export { type Config, ERROR_NO_COMMAND, ERROR_PORT_USED, ERROR_TIMEOUT, JestDevServerError, setup, teardown };
package/dist/index.js ADDED
@@ -0,0 +1,213 @@
1
+ 'use strict';
2
+
3
+ var node_stream = require('node:stream');
4
+ var node_net = require('node:net');
5
+ var node_util = require('node:util');
6
+ var chalk = require('chalk');
7
+ var spawnd = require('spawnd');
8
+ var cwd = require('cwd');
9
+ var waitOn = require('wait-on');
10
+ var findProcess = require('find-process');
11
+ var treeKill = require('@ohos-ports/tree-kill');
12
+ var prompts = require('prompts');
13
+
14
+ const DEFAULT_CONFIG = {
15
+ debug: false,
16
+ options: {},
17
+ launchTimeout: 5000,
18
+ host: undefined,
19
+ port: undefined,
20
+ protocol: "tcp",
21
+ usedPortAction: "ask",
22
+ waitOnScheme: undefined
23
+ };
24
+ const resolveConfig = (config)=>{
25
+ return {
26
+ ...DEFAULT_CONFIG,
27
+ ...config
28
+ };
29
+ };
30
+ const pTreeKill = node_util.promisify(treeKill);
31
+ const serverLogPrefixer = new node_stream.Transform({
32
+ transform (chunk, _encoding, callback) {
33
+ this.push(chalk.magentaBright(`[jest-dev-server] ${chunk.toString()}`));
34
+ callback();
35
+ }
36
+ });
37
+ const ERROR_TIMEOUT = "ERROR_TIMEOUT";
38
+ const ERROR_PORT_USED = "ERROR_PORT_USED";
39
+ const ERROR_NO_COMMAND = "ERROR_NO_COMMAND";
40
+ class JestDevServerError extends Error {
41
+ code;
42
+ constructor(message, options){
43
+ // @ts-ignore - cause is not part of the Error constructor (yet)
44
+ super(message, options?.cause ? {
45
+ cause: options.cause
46
+ } : undefined);
47
+ this.code = options?.code;
48
+ }
49
+ }
50
+ const logProcDetection = (name, port)=>{
51
+ console.log(chalk.blue(`🕵️ Detecting a process "${name}" running on port "${port}"`));
52
+ };
53
+ const killProc = async (proc)=>{
54
+ console.log(chalk.yellow(`Killing process ${proc.name}...`));
55
+ await pTreeKill(proc.pid);
56
+ console.log(chalk.green(`Successfully killed process ${proc.name}`));
57
+ };
58
+ const spawnServer = (config)=>{
59
+ if (!config.command) {
60
+ throw new JestDevServerError("You must define a `command`", {
61
+ code: ERROR_NO_COMMAND
62
+ });
63
+ }
64
+ const proc = spawnd.spawnd(config.command, {
65
+ shell: true,
66
+ env: process.env,
67
+ cwd: cwd(),
68
+ ...config.options
69
+ });
70
+ if (config.debug) {
71
+ console.log(chalk.magentaBright("\nJest dev-server output:"));
72
+ proc.stdout.pipe(serverLogPrefixer).pipe(process.stdout);
73
+ } else {
74
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
75
+ proc.stdout.on("data", ()=>{});
76
+ }
77
+ return proc;
78
+ };
79
+ const outOfStin = async (run)=>{
80
+ const { stdin } = process;
81
+ const listeners = stdin.listeners("data");
82
+ const result = await run();
83
+ // @ts-ignore
84
+ listeners.forEach((listener)=>stdin.on("data", listener));
85
+ stdin.setRawMode(true);
86
+ stdin.setEncoding("utf8");
87
+ stdin.resume();
88
+ return result;
89
+ };
90
+ const checkIsPortBusy = async (config)=>{
91
+ return new Promise((resolve)=>{
92
+ const server = node_net.createServer().once("error", (err)=>{
93
+ if (err.code === "EADDRINUSE") {
94
+ resolve(true);
95
+ } else {
96
+ resolve(false);
97
+ }
98
+ }).once("listening", ()=>{
99
+ server.once("close", ()=>resolve(false)).close();
100
+ }).listen(config.port, config.host);
101
+ });
102
+ };
103
+ const usedPortHandlers = {
104
+ error: (port)=>{
105
+ throw new JestDevServerError(`Port ${port} is in use`, {
106
+ code: ERROR_PORT_USED
107
+ });
108
+ },
109
+ kill: async (port)=>{
110
+ console.log("");
111
+ console.log(`Killing process listening to ${port}. On linux, this may require you to enter your password.`);
112
+ const [portProcess] = await findProcess("port", port);
113
+ logProcDetection(portProcess.name, port);
114
+ await killProc(portProcess);
115
+ return true;
116
+ },
117
+ ask: async (port)=>{
118
+ console.log("");
119
+ const answers = await outOfStin(()=>prompts({
120
+ type: "confirm",
121
+ name: "kill",
122
+ message: `Another process is listening on ${port}. Should I kill it for you? On linux, this may require you to enter your password.`,
123
+ initial: true
124
+ }));
125
+ if (answers.kill) {
126
+ const [portProcess] = await findProcess("port", port);
127
+ logProcDetection(portProcess.name, port);
128
+ await killProc(portProcess);
129
+ return true;
130
+ }
131
+ process.exit(1);
132
+ },
133
+ ignore: (port)=>{
134
+ console.log("");
135
+ console.log(`Port ${port} is already used. Assuming server is already running.`);
136
+ return false;
137
+ }
138
+ };
139
+ const handleUsedPort = async (config)=>{
140
+ if (config.port === undefined) return true;
141
+ if (!config.usedPortAction) {
142
+ throw new JestDevServerError(`Port ${config.port} is in use, but no action was provided to handle it. Please provide a "usedPortAction" in your config.`);
143
+ }
144
+ const isPortBusy = await checkIsPortBusy(config);
145
+ if (isPortBusy) {
146
+ const usedPortHandler = usedPortHandlers[config.usedPortAction];
147
+ return await usedPortHandler(config.port);
148
+ }
149
+ return true;
150
+ };
151
+ const checkIsTimeoutError = (err)=>{
152
+ return Boolean(err?.message?.startsWith("Timed out waiting for"));
153
+ };
154
+ const waitForServerToBeReady = async (config)=>{
155
+ if (config.port === undefined) return;
156
+ const { launchTimeout, protocol, host, port, path, waitOnScheme } = config;
157
+ let resource = `${host ?? "0.0.0.0"}:${port}`;
158
+ if (path) {
159
+ resource = `${resource}/${path}`;
160
+ }
161
+ let url;
162
+ if (protocol === "tcp" || protocol === "socket") {
163
+ url = `${protocol}:${resource}`;
164
+ } else {
165
+ url = `${protocol}://${resource}`;
166
+ }
167
+ const opts = {
168
+ resources: [
169
+ url
170
+ ],
171
+ timeout: launchTimeout,
172
+ ...waitOnScheme
173
+ };
174
+ try {
175
+ await waitOn(opts);
176
+ } catch (err) {
177
+ if (checkIsTimeoutError(err)) {
178
+ throw new JestDevServerError(`Server has taken more than ${launchTimeout}ms to start.`, {
179
+ code: ERROR_TIMEOUT
180
+ });
181
+ }
182
+ throw err;
183
+ }
184
+ };
185
+ const setupJestServer = async (providedConfig)=>{
186
+ const config = resolveConfig(providedConfig);
187
+ const shouldRunServer = await handleUsedPort(config);
188
+ if (shouldRunServer) {
189
+ const proc = spawnServer(config);
190
+ await waitForServerToBeReady(config);
191
+ return proc;
192
+ }
193
+ return null;
194
+ };
195
+ async function setup(providedConfigs) {
196
+ const configs = Array.isArray(providedConfigs) ? providedConfigs : [
197
+ providedConfigs
198
+ ];
199
+ const procs = await Promise.all(configs.map((config)=>setupJestServer(config)));
200
+ return procs.filter(Boolean);
201
+ }
202
+ async function teardown(procs) {
203
+ if (procs.length) {
204
+ await Promise.all(procs.map((proc)=>proc.destroy()));
205
+ }
206
+ }
207
+
208
+ exports.ERROR_NO_COMMAND = ERROR_NO_COMMAND;
209
+ exports.ERROR_PORT_USED = ERROR_PORT_USED;
210
+ exports.ERROR_TIMEOUT = ERROR_TIMEOUT;
211
+ exports.JestDevServerError = JestDevServerError;
212
+ exports.setup = setup;
213
+ exports.teardown = teardown;
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
2
  "name": "@ohos-ports/jest-dev-server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "Starts a server before your Jest tests and tears it down after.",
4
+ "version": "11.0.0-beta.0",
5
+ "type": "commonjs",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/ohos-ports/ohos-ports.git",
18
+ "directory": "ports/jest-dev-server/11.0.0"
19
+ },
20
+ "license": "MIT",
21
+ "homepage": "https://github.com/argos-ci/jest-puppeteer/tree/main/packages/jest-dev-server#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/argos-ci/jest-puppeteer/issues"
24
+ },
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "keywords": [
29
+ "jest",
30
+ "jest-environment",
31
+ "server"
32
+ ],
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "scripts": {
37
+ "prebuild": "rm -rf dist",
38
+ "build": "rollup -c"
39
+ },
40
+ "devDependencies": {
41
+ "@types/cwd": "^0.10.2",
42
+ "@types/prompts": "^2.4.9",
43
+ "@types/wait-on": "^5.3.4",
44
+ "@ohos-ports/rollup": "^4.62.2-beta.2",
45
+ "rollup-plugin-dts": "^6.1.1",
46
+ "@ohos-ports/rollup-plugin-swc3": "^0.12.1-beta.1"
47
+ },
48
+ "dependencies": {
49
+ "chalk": "^4.1.2",
50
+ "cwd": "^0.10.0",
51
+ "find-process": "^1.4.7",
52
+ "prompts": "^2.4.2",
53
+ "spawnd": "^11.0.0",
54
+ "@ohos-ports/tree-kill": "^1.2.2-beta.0",
55
+ "wait-on": "^8.0.1"
56
+ },
57
+ "gitHead": "ddb3fb9d80b2d38d27b327c0d857280edf877f3e"
58
+ }