@get-skipper/jest 1.0.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 +21 -0
- package/README.md +59 -0
- package/dist/index.d.mts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +64 -0
- package/dist/index.mjs.map +1 -0
- package/dist/setup.js +95 -0
- package/dist/setup.js.map +1 -0
- package/dist/setup.mjs +71 -0
- package/dist/setup.mjs.map +1 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Skipper Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @get-skipper/jest
|
|
2
|
+
|
|
3
|
+
Skipper plugin for [Jest](https://jestjs.io/) — enable/disable tests from a Google Spreadsheet.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add -D @get-skipper/jest
|
|
9
|
+
# or
|
|
10
|
+
npm install --save-dev @get-skipper/jest
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Setup
|
|
14
|
+
|
|
15
|
+
Update `jest.config.ts` — this is the **only change required**:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createSkipperGlobalSetup, createSkipperGlobalTeardown, setupFile } from '@get-skipper/jest';
|
|
19
|
+
|
|
20
|
+
const skipperConfig = {
|
|
21
|
+
spreadsheetId: process.env.SKIPPER_SPREADSHEET_ID!,
|
|
22
|
+
credentials: { credentialsBase64: process.env.GOOGLE_CREDS_B64! },
|
|
23
|
+
// Or for local dev:
|
|
24
|
+
// credentials: { credentialsFile: './service-account.json' },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default {
|
|
28
|
+
globalSetup: createSkipperGlobalSetup(skipperConfig),
|
|
29
|
+
globalTeardown: createSkipperGlobalTeardown(skipperConfig),
|
|
30
|
+
setupFilesAfterFramework: [setupFile],
|
|
31
|
+
// ... rest of your config
|
|
32
|
+
};
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
No changes to test files are required. Tests with a future `disabledUntil` date are automatically skipped.
|
|
36
|
+
|
|
37
|
+
## Test ID Format
|
|
38
|
+
|
|
39
|
+
Tests are identified in the spreadsheet as:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
{relative file path} > {describe block(s)} > {test name}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
```
|
|
47
|
+
tests/auth/login.test.ts > login > should log in with valid credentials
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Modes
|
|
51
|
+
|
|
52
|
+
- **`read-only`** (default): reads the spreadsheet, skips disabled tests. No writes.
|
|
53
|
+
- **`sync`** (`SKIPPER_MODE=sync`): same as read-only + reconciles the spreadsheet after the run.
|
|
54
|
+
|
|
55
|
+
See the [root README](../../README.md) for full setup instructions.
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SkipperConfig } from '@get-skipper/core';
|
|
2
|
+
export { SkipperConfig } from '@get-skipper/core';
|
|
3
|
+
|
|
4
|
+
declare function createSkipperGlobalSetup(config: SkipperConfig): () => Promise<void>;
|
|
5
|
+
|
|
6
|
+
declare function createSkipperGlobalTeardown(config: SkipperConfig): () => Promise<void>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Absolute path to the setup file.
|
|
10
|
+
* Add this to `setupFilesAfterFramework` in jest.config.ts:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { setupFile } from '@get-skipper/jest';
|
|
14
|
+
* export default { setupFilesAfterFramework: [setupFile] };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare const setupFile: string;
|
|
18
|
+
|
|
19
|
+
export { createSkipperGlobalSetup, createSkipperGlobalTeardown, setupFile };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SkipperConfig } from '@get-skipper/core';
|
|
2
|
+
export { SkipperConfig } from '@get-skipper/core';
|
|
3
|
+
|
|
4
|
+
declare function createSkipperGlobalSetup(config: SkipperConfig): () => Promise<void>;
|
|
5
|
+
|
|
6
|
+
declare function createSkipperGlobalTeardown(config: SkipperConfig): () => Promise<void>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Absolute path to the setup file.
|
|
10
|
+
* Add this to `setupFilesAfterFramework` in jest.config.ts:
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { setupFile } from '@get-skipper/jest';
|
|
14
|
+
* export default { setupFilesAfterFramework: [setupFile] };
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
declare const setupFile: string;
|
|
18
|
+
|
|
19
|
+
export { createSkipperGlobalSetup, createSkipperGlobalTeardown, setupFile };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
createSkipperGlobalSetup: () => createSkipperGlobalSetup,
|
|
34
|
+
createSkipperGlobalTeardown: () => createSkipperGlobalTeardown,
|
|
35
|
+
setupFile: () => setupFile
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
var path3 = __toESM(require("path"));
|
|
39
|
+
|
|
40
|
+
// src/globalSetup.ts
|
|
41
|
+
var fs = __toESM(require("fs"));
|
|
42
|
+
var os = __toESM(require("os"));
|
|
43
|
+
var path = __toESM(require("path"));
|
|
44
|
+
var import_core = require("@get-skipper/core");
|
|
45
|
+
function createSkipperGlobalSetup(config) {
|
|
46
|
+
return async function skipperGlobalSetup() {
|
|
47
|
+
const resolver = new import_core.SkipperResolver(config);
|
|
48
|
+
await resolver.initialize();
|
|
49
|
+
const discoveredDir = fs.mkdtempSync(path.join(os.tmpdir(), "skipper-"));
|
|
50
|
+
const cacheFile = path.join(discoveredDir, "cache.json");
|
|
51
|
+
fs.writeFileSync(cacheFile, JSON.stringify(resolver.toJSON()), "utf8");
|
|
52
|
+
process.env.SKIPPER_CACHE_FILE = cacheFile;
|
|
53
|
+
process.env.SKIPPER_DISCOVERED_DIR = discoveredDir;
|
|
54
|
+
(0, import_core.log)("[skipper] Spreadsheet loaded.");
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/globalTeardown.ts
|
|
59
|
+
var fs2 = __toESM(require("fs"));
|
|
60
|
+
var path2 = __toESM(require("path"));
|
|
61
|
+
var import_core2 = require("@get-skipper/core");
|
|
62
|
+
function createSkipperGlobalTeardown(config) {
|
|
63
|
+
return async function skipperGlobalTeardown() {
|
|
64
|
+
if (process.env.SKIPPER_MODE !== "sync") return;
|
|
65
|
+
const discoveredDir = process.env.SKIPPER_DISCOVERED_DIR;
|
|
66
|
+
if (!discoveredDir || !fs2.existsSync(discoveredDir)) {
|
|
67
|
+
(0, import_core2.warn)("[skipper] No discovered tests found \u2014 skipping spreadsheet sync.");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const files = fs2.readdirSync(discoveredDir).filter((f) => f.endsWith(".json") && f !== "cache.json");
|
|
71
|
+
if (files.length === 0) {
|
|
72
|
+
(0, import_core2.warn)("[skipper] No discovered tests found \u2014 skipping spreadsheet sync.");
|
|
73
|
+
fs2.rmSync(discoveredDir, { recursive: true, force: true });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const allIds = /* @__PURE__ */ new Set();
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
try {
|
|
79
|
+
const ids = JSON.parse(fs2.readFileSync(path2.join(discoveredDir, file), "utf8"));
|
|
80
|
+
ids.forEach((id) => allIds.add(id));
|
|
81
|
+
} catch {
|
|
82
|
+
(0, import_core2.warn)(`[skipper] Failed to parse ${file} \u2014 skipping.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
fs2.rmSync(discoveredDir, { recursive: true, force: true });
|
|
86
|
+
const discoveredIds = [...allIds];
|
|
87
|
+
(0, import_core2.log)(`[skipper] Syncing ${discoveredIds.length} test(s) to spreadsheet\u2026`);
|
|
88
|
+
const writer = new import_core2.SheetsWriter(config);
|
|
89
|
+
await writer.sync(discoveredIds);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/index.ts
|
|
94
|
+
var setupFile = path3.resolve(__dirname, "setup.js");
|
|
95
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
96
|
+
0 && (module.exports = {
|
|
97
|
+
createSkipperGlobalSetup,
|
|
98
|
+
createSkipperGlobalTeardown,
|
|
99
|
+
setupFile
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/globalSetup.ts","../src/globalTeardown.ts"],"sourcesContent":["import * as path from 'path';\n\nexport { createSkipperGlobalSetup } from './globalSetup';\nexport { createSkipperGlobalTeardown } from './globalTeardown';\nexport type { SkipperConfig } from '@get-skipper/core';\n\n/**\n * Absolute path to the setup file.\n * Add this to `setupFilesAfterFramework` in jest.config.ts:\n *\n * ```ts\n * import { setupFile } from '@get-skipper/jest';\n * export default { setupFilesAfterFramework: [setupFile] };\n * ```\n */\nexport const setupFile = path.resolve(__dirname, 'setup.js');\n","import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport { SkipperResolver, log } from '@get-skipper/core';\nimport type { SkipperConfig } from '@get-skipper/core';\n\nexport function createSkipperGlobalSetup(config: SkipperConfig) {\n return async function skipperGlobalSetup(): Promise<void> {\n const resolver = new SkipperResolver(config);\n await resolver.initialize();\n\n // Create a temp directory for:\n // 1. The resolver cache (shared with all worker processes via SKIPPER_CACHE_FILE)\n // 2. Per-worker discovered test ID files (merged by globalTeardown)\n //\n // Using a file instead of process.env.SKIPPER_CACHE avoids OS env-var size\n // limits for large test suites (some systems cap env vars at 2 MB).\n const discoveredDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skipper-'));\n const cacheFile = path.join(discoveredDir, 'cache.json');\n\n fs.writeFileSync(cacheFile, JSON.stringify(resolver.toJSON()), 'utf8');\n\n // Jest propagates process.env mutations from globalSetup to all worker processes.\n process.env.SKIPPER_CACHE_FILE = cacheFile;\n process.env.SKIPPER_DISCOVERED_DIR = discoveredDir;\n\n log('[skipper] Spreadsheet loaded.');\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport { SheetsWriter, log, warn } from '@get-skipper/core';\nimport type { SkipperConfig } from '@get-skipper/core';\n\nexport function createSkipperGlobalTeardown(config: SkipperConfig) {\n return async function skipperGlobalTeardown(): Promise<void> {\n if (process.env.SKIPPER_MODE !== 'sync') return;\n\n // Workers write their discovered test IDs as JSON files into SKIPPER_DISCOVERED_DIR.\n // Merge all files here (globalTeardown runs in its own process, separate from workers).\n const discoveredDir = process.env.SKIPPER_DISCOVERED_DIR;\n if (!discoveredDir || !fs.existsSync(discoveredDir)) {\n warn('[skipper] No discovered tests found — skipping spreadsheet sync.');\n return;\n }\n\n // cache.json is the resolver snapshot written by globalSetup — skip it.\n const files = fs.readdirSync(discoveredDir).filter(f => f.endsWith('.json') && f !== 'cache.json');\n if (files.length === 0) {\n warn('[skipper] No discovered tests found — skipping spreadsheet sync.');\n fs.rmSync(discoveredDir, { recursive: true, force: true });\n return;\n }\n\n const allIds = new Set<string>();\n for (const file of files) {\n try {\n const ids = JSON.parse(fs.readFileSync(path.join(discoveredDir, file), 'utf8')) as string[];\n ids.forEach(id => allIds.add(id));\n } catch {\n warn(`[skipper] Failed to parse ${file} — skipping.`);\n }\n }\n\n // Clean up temp directory\n fs.rmSync(discoveredDir, { recursive: true, force: true });\n\n const discoveredIds = [...allIds];\n log(`[skipper] Syncing ${discoveredIds.length} test(s) to spreadsheet…`);\n const writer = new SheetsWriter(config);\n await writer.sync(discoveredIds);\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,QAAsB;;;ACAtB,SAAoB;AACpB,SAAoB;AACpB,WAAsB;AACtB,kBAAqC;AAG9B,SAAS,yBAAyB,QAAuB;AAC9D,SAAO,eAAe,qBAAoC;AACxD,UAAM,WAAW,IAAI,4BAAgB,MAAM;AAC3C,UAAM,SAAS,WAAW;AAQ1B,UAAM,gBAAmB,eAAiB,UAAQ,UAAO,GAAG,UAAU,CAAC;AACvE,UAAM,YAAiB,UAAK,eAAe,YAAY;AAEvD,IAAG,iBAAc,WAAW,KAAK,UAAU,SAAS,OAAO,CAAC,GAAG,MAAM;AAGrE,YAAQ,IAAI,qBAAqB;AACjC,YAAQ,IAAI,yBAAyB;AAErC,yBAAI,+BAA+B;AAAA,EACrC;AACF;;;AC5BA,IAAAC,MAAoB;AACpB,IAAAC,QAAsB;AACtB,IAAAC,eAAwC;AAGjC,SAAS,4BAA4B,QAAuB;AACjE,SAAO,eAAe,wBAAuC;AAC3D,QAAI,QAAQ,IAAI,iBAAiB,OAAQ;AAIzC,UAAM,gBAAgB,QAAQ,IAAI;AAClC,QAAI,CAAC,iBAAiB,CAAI,eAAW,aAAa,GAAG;AACnD,6BAAK,uEAAkE;AACvE;AAAA,IACF;AAGA,UAAM,QAAW,gBAAY,aAAa,EAAE,OAAO,OAAK,EAAE,SAAS,OAAO,KAAK,MAAM,YAAY;AACjG,QAAI,MAAM,WAAW,GAAG;AACtB,6BAAK,uEAAkE;AACvE,MAAG,WAAO,eAAe,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACzD;AAAA,IACF;AAEA,UAAM,SAAS,oBAAI,IAAY;AAC/B,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,MAAM,KAAK,MAAS,iBAAkB,WAAK,eAAe,IAAI,GAAG,MAAM,CAAC;AAC9E,YAAI,QAAQ,QAAM,OAAO,IAAI,EAAE,CAAC;AAAA,MAClC,QAAQ;AACN,+BAAK,6BAA6B,IAAI,mBAAc;AAAA,MACtD;AAAA,IACF;AAGA,IAAG,WAAO,eAAe,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAEzD,UAAM,gBAAgB,CAAC,GAAG,MAAM;AAChC,0BAAI,qBAAqB,cAAc,MAAM,+BAA0B;AACvE,UAAM,SAAS,IAAI,0BAAa,MAAM;AACtC,UAAM,OAAO,KAAK,aAAa;AAAA,EACjC;AACF;;;AF5BO,IAAM,YAAiB,cAAQ,WAAW,UAAU;","names":["path","fs","path","import_core"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import * as path3 from "path";
|
|
3
|
+
|
|
4
|
+
// src/globalSetup.ts
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as os from "os";
|
|
7
|
+
import * as path from "path";
|
|
8
|
+
import { SkipperResolver, log } from "@get-skipper/core";
|
|
9
|
+
function createSkipperGlobalSetup(config) {
|
|
10
|
+
return async function skipperGlobalSetup() {
|
|
11
|
+
const resolver = new SkipperResolver(config);
|
|
12
|
+
await resolver.initialize();
|
|
13
|
+
const discoveredDir = fs.mkdtempSync(path.join(os.tmpdir(), "skipper-"));
|
|
14
|
+
const cacheFile = path.join(discoveredDir, "cache.json");
|
|
15
|
+
fs.writeFileSync(cacheFile, JSON.stringify(resolver.toJSON()), "utf8");
|
|
16
|
+
process.env.SKIPPER_CACHE_FILE = cacheFile;
|
|
17
|
+
process.env.SKIPPER_DISCOVERED_DIR = discoveredDir;
|
|
18
|
+
log("[skipper] Spreadsheet loaded.");
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/globalTeardown.ts
|
|
23
|
+
import * as fs2 from "fs";
|
|
24
|
+
import * as path2 from "path";
|
|
25
|
+
import { SheetsWriter, log as log2, warn } from "@get-skipper/core";
|
|
26
|
+
function createSkipperGlobalTeardown(config) {
|
|
27
|
+
return async function skipperGlobalTeardown() {
|
|
28
|
+
if (process.env.SKIPPER_MODE !== "sync") return;
|
|
29
|
+
const discoveredDir = process.env.SKIPPER_DISCOVERED_DIR;
|
|
30
|
+
if (!discoveredDir || !fs2.existsSync(discoveredDir)) {
|
|
31
|
+
warn("[skipper] No discovered tests found \u2014 skipping spreadsheet sync.");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const files = fs2.readdirSync(discoveredDir).filter((f) => f.endsWith(".json") && f !== "cache.json");
|
|
35
|
+
if (files.length === 0) {
|
|
36
|
+
warn("[skipper] No discovered tests found \u2014 skipping spreadsheet sync.");
|
|
37
|
+
fs2.rmSync(discoveredDir, { recursive: true, force: true });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const allIds = /* @__PURE__ */ new Set();
|
|
41
|
+
for (const file of files) {
|
|
42
|
+
try {
|
|
43
|
+
const ids = JSON.parse(fs2.readFileSync(path2.join(discoveredDir, file), "utf8"));
|
|
44
|
+
ids.forEach((id) => allIds.add(id));
|
|
45
|
+
} catch {
|
|
46
|
+
warn(`[skipper] Failed to parse ${file} \u2014 skipping.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
fs2.rmSync(discoveredDir, { recursive: true, force: true });
|
|
50
|
+
const discoveredIds = [...allIds];
|
|
51
|
+
log2(`[skipper] Syncing ${discoveredIds.length} test(s) to spreadsheet\u2026`);
|
|
52
|
+
const writer = new SheetsWriter(config);
|
|
53
|
+
await writer.sync(discoveredIds);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/index.ts
|
|
58
|
+
var setupFile = path3.resolve(__dirname, "setup.js");
|
|
59
|
+
export {
|
|
60
|
+
createSkipperGlobalSetup,
|
|
61
|
+
createSkipperGlobalTeardown,
|
|
62
|
+
setupFile
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/globalSetup.ts","../src/globalTeardown.ts"],"sourcesContent":["import * as path from 'path';\n\nexport { createSkipperGlobalSetup } from './globalSetup';\nexport { createSkipperGlobalTeardown } from './globalTeardown';\nexport type { SkipperConfig } from '@get-skipper/core';\n\n/**\n * Absolute path to the setup file.\n * Add this to `setupFilesAfterFramework` in jest.config.ts:\n *\n * ```ts\n * import { setupFile } from '@get-skipper/jest';\n * export default { setupFilesAfterFramework: [setupFile] };\n * ```\n */\nexport const setupFile = path.resolve(__dirname, 'setup.js');\n","import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport { SkipperResolver, log } from '@get-skipper/core';\nimport type { SkipperConfig } from '@get-skipper/core';\n\nexport function createSkipperGlobalSetup(config: SkipperConfig) {\n return async function skipperGlobalSetup(): Promise<void> {\n const resolver = new SkipperResolver(config);\n await resolver.initialize();\n\n // Create a temp directory for:\n // 1. The resolver cache (shared with all worker processes via SKIPPER_CACHE_FILE)\n // 2. Per-worker discovered test ID files (merged by globalTeardown)\n //\n // Using a file instead of process.env.SKIPPER_CACHE avoids OS env-var size\n // limits for large test suites (some systems cap env vars at 2 MB).\n const discoveredDir = fs.mkdtempSync(path.join(os.tmpdir(), 'skipper-'));\n const cacheFile = path.join(discoveredDir, 'cache.json');\n\n fs.writeFileSync(cacheFile, JSON.stringify(resolver.toJSON()), 'utf8');\n\n // Jest propagates process.env mutations from globalSetup to all worker processes.\n process.env.SKIPPER_CACHE_FILE = cacheFile;\n process.env.SKIPPER_DISCOVERED_DIR = discoveredDir;\n\n log('[skipper] Spreadsheet loaded.');\n };\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport { SheetsWriter, log, warn } from '@get-skipper/core';\nimport type { SkipperConfig } from '@get-skipper/core';\n\nexport function createSkipperGlobalTeardown(config: SkipperConfig) {\n return async function skipperGlobalTeardown(): Promise<void> {\n if (process.env.SKIPPER_MODE !== 'sync') return;\n\n // Workers write their discovered test IDs as JSON files into SKIPPER_DISCOVERED_DIR.\n // Merge all files here (globalTeardown runs in its own process, separate from workers).\n const discoveredDir = process.env.SKIPPER_DISCOVERED_DIR;\n if (!discoveredDir || !fs.existsSync(discoveredDir)) {\n warn('[skipper] No discovered tests found — skipping spreadsheet sync.');\n return;\n }\n\n // cache.json is the resolver snapshot written by globalSetup — skip it.\n const files = fs.readdirSync(discoveredDir).filter(f => f.endsWith('.json') && f !== 'cache.json');\n if (files.length === 0) {\n warn('[skipper] No discovered tests found — skipping spreadsheet sync.');\n fs.rmSync(discoveredDir, { recursive: true, force: true });\n return;\n }\n\n const allIds = new Set<string>();\n for (const file of files) {\n try {\n const ids = JSON.parse(fs.readFileSync(path.join(discoveredDir, file), 'utf8')) as string[];\n ids.forEach(id => allIds.add(id));\n } catch {\n warn(`[skipper] Failed to parse ${file} — skipping.`);\n }\n }\n\n // Clean up temp directory\n fs.rmSync(discoveredDir, { recursive: true, force: true });\n\n const discoveredIds = [...allIds];\n log(`[skipper] Syncing ${discoveredIds.length} test(s) to spreadsheet…`);\n const writer = new SheetsWriter(config);\n await writer.sync(discoveredIds);\n };\n}\n"],"mappings":";AAAA,YAAYA,WAAU;;;ACAtB,YAAY,QAAQ;AACpB,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,iBAAiB,WAAW;AAG9B,SAAS,yBAAyB,QAAuB;AAC9D,SAAO,eAAe,qBAAoC;AACxD,UAAM,WAAW,IAAI,gBAAgB,MAAM;AAC3C,UAAM,SAAS,WAAW;AAQ1B,UAAM,gBAAmB,eAAiB,UAAQ,UAAO,GAAG,UAAU,CAAC;AACvE,UAAM,YAAiB,UAAK,eAAe,YAAY;AAEvD,IAAG,iBAAc,WAAW,KAAK,UAAU,SAAS,OAAO,CAAC,GAAG,MAAM;AAGrE,YAAQ,IAAI,qBAAqB;AACjC,YAAQ,IAAI,yBAAyB;AAErC,QAAI,+BAA+B;AAAA,EACrC;AACF;;;AC5BA,YAAYC,SAAQ;AACpB,YAAYC,WAAU;AACtB,SAAS,cAAc,OAAAC,MAAK,YAAY;AAGjC,SAAS,4BAA4B,QAAuB;AACjE,SAAO,eAAe,wBAAuC;AAC3D,QAAI,QAAQ,IAAI,iBAAiB,OAAQ;AAIzC,UAAM,gBAAgB,QAAQ,IAAI;AAClC,QAAI,CAAC,iBAAiB,CAAI,eAAW,aAAa,GAAG;AACnD,WAAK,uEAAkE;AACvE;AAAA,IACF;AAGA,UAAM,QAAW,gBAAY,aAAa,EAAE,OAAO,OAAK,EAAE,SAAS,OAAO,KAAK,MAAM,YAAY;AACjG,QAAI,MAAM,WAAW,GAAG;AACtB,WAAK,uEAAkE;AACvE,MAAG,WAAO,eAAe,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AACzD;AAAA,IACF;AAEA,UAAM,SAAS,oBAAI,IAAY;AAC/B,eAAW,QAAQ,OAAO;AACxB,UAAI;AACF,cAAM,MAAM,KAAK,MAAS,iBAAkB,WAAK,eAAe,IAAI,GAAG,MAAM,CAAC;AAC9E,YAAI,QAAQ,QAAM,OAAO,IAAI,EAAE,CAAC;AAAA,MAClC,QAAQ;AACN,aAAK,6BAA6B,IAAI,mBAAc;AAAA,MACtD;AAAA,IACF;AAGA,IAAG,WAAO,eAAe,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAEzD,UAAM,gBAAgB,CAAC,GAAG,MAAM;AAChC,IAAAA,KAAI,qBAAqB,cAAc,MAAM,+BAA0B;AACvE,UAAM,SAAS,IAAI,aAAa,MAAM;AACtC,UAAM,OAAO,KAAK,aAAa;AAAA,EACjC;AACF;;;AF5BO,IAAM,YAAiB,cAAQ,WAAW,UAAU;","names":["path","fs","path","log"]}
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// src/setup.ts
|
|
26
|
+
var fs = __toESM(require("fs"));
|
|
27
|
+
var path = __toESM(require("path"));
|
|
28
|
+
var import_core = require("@get-skipper/core");
|
|
29
|
+
var g = global;
|
|
30
|
+
var describeStack = [];
|
|
31
|
+
function getResolver() {
|
|
32
|
+
const cacheFile = process.env.SKIPPER_CACHE_FILE;
|
|
33
|
+
if (!cacheFile) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
"[skipper] SKIPPER_CACHE_FILE is not set. Did you add createSkipperGlobalSetup() to globalSetup in jest.config?"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const raw = fs.readFileSync(cacheFile, "utf8");
|
|
39
|
+
return import_core.SkipperResolver.fromJSON(JSON.parse(raw));
|
|
40
|
+
}
|
|
41
|
+
var resolver = getResolver();
|
|
42
|
+
var discoveredIds = [];
|
|
43
|
+
function appendDiscovered(testId) {
|
|
44
|
+
discoveredIds.push(testId);
|
|
45
|
+
}
|
|
46
|
+
var discoveredDir = process.env.SKIPPER_DISCOVERED_DIR;
|
|
47
|
+
if (discoveredDir) {
|
|
48
|
+
const workerId = process.env.JEST_WORKER_ID ?? "0";
|
|
49
|
+
const originalAfterAll = g.afterAll;
|
|
50
|
+
if (originalAfterAll) {
|
|
51
|
+
originalAfterAll(() => {
|
|
52
|
+
if (discoveredIds.length === 0) return;
|
|
53
|
+
const suffix = `${workerId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
54
|
+
fs.writeFileSync(
|
|
55
|
+
path.join(discoveredDir, `${suffix}.json`),
|
|
56
|
+
JSON.stringify(discoveredIds)
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function buildCurrentTestId(name) {
|
|
62
|
+
const testPath = g.expect?.getState?.()?.testPath ?? "";
|
|
63
|
+
return (0, import_core.buildTestId)(testPath, [...describeStack, name]);
|
|
64
|
+
}
|
|
65
|
+
var originalDescribe = g.describe;
|
|
66
|
+
if (originalDescribe) {
|
|
67
|
+
g.describe = Object.assign(
|
|
68
|
+
(name, fn) => {
|
|
69
|
+
describeStack.push(name);
|
|
70
|
+
originalDescribe(name, () => {
|
|
71
|
+
try {
|
|
72
|
+
fn();
|
|
73
|
+
} finally {
|
|
74
|
+
describeStack.pop();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
originalDescribe
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
var originalTest = g.test;
|
|
82
|
+
if (originalTest) {
|
|
83
|
+
const wrapped = Object.assign(
|
|
84
|
+
(name, fn, timeout) => {
|
|
85
|
+
if (fn === void 0) return originalTest(name, fn, timeout);
|
|
86
|
+
const testId = buildCurrentTestId(name);
|
|
87
|
+
appendDiscovered(testId);
|
|
88
|
+
return resolver.isTestEnabled(testId) ? originalTest(name, fn, timeout) : originalTest.skip(name, fn, timeout);
|
|
89
|
+
},
|
|
90
|
+
originalTest
|
|
91
|
+
);
|
|
92
|
+
g.test = wrapped;
|
|
93
|
+
g.it = wrapped;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/setup.ts"],"sourcesContent":["/**\n * Jest setup file — add to `setupFilesAfterEnv` in jest.config.ts.\n *\n * This file:\n * 1. Rehydrates the SkipperResolver from the cache file written by globalSetup\n * 2. Overrides global `test` and `it` to auto-skip disabled tests\n * 3. Collects all discovered test IDs and writes them to SKIPPER_DISCOVERED_DIR\n * after each test file, so globalTeardown can merge them across workers.\n *\n * Uses (global as any) casts to avoid depending on @types/jest.\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { SkipperResolver, buildTestId } from '@get-skipper/core';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst g = global as any;\n\nconst describeStack: string[] = [];\n\nfunction getResolver(): SkipperResolver {\n const cacheFile = process.env.SKIPPER_CACHE_FILE;\n if (!cacheFile) {\n throw new Error(\n '[skipper] SKIPPER_CACHE_FILE is not set. ' +\n 'Did you add createSkipperGlobalSetup() to globalSetup in jest.config?',\n );\n }\n const raw = fs.readFileSync(cacheFile, 'utf8');\n return SkipperResolver.fromJSON(JSON.parse(raw) as Record<string, string | null>);\n}\n\nconst resolver = getResolver();\n\n// Collect discovered test IDs in memory; flush to disk in afterAll so\n// globalTeardown (a separate process) can read them across all workers.\nconst discoveredIds: string[] = [];\n\nfunction appendDiscovered(testId: string): void {\n discoveredIds.push(testId);\n}\n\n// Register a root-level afterAll to flush this worker's discoveries to disk.\n// SKIPPER_DISCOVERED_DIR is set by globalSetup and propagated to all workers.\nconst discoveredDir = process.env.SKIPPER_DISCOVERED_DIR;\nif (discoveredDir) {\n const workerId = process.env.JEST_WORKER_ID ?? '0';\n const originalAfterAll = g.afterAll as ((fn: () => void) => void) | undefined;\n if (originalAfterAll) {\n originalAfterAll(() => {\n if (discoveredIds.length === 0) return;\n // Use worker ID + timestamp + random to guarantee a unique file name per test file.\n const suffix = `${workerId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n fs.writeFileSync(\n path.join(discoveredDir, `${suffix}.json`),\n JSON.stringify(discoveredIds),\n );\n });\n }\n}\n\nfunction buildCurrentTestId(name: string): string {\n // `expect.getState().testPath` is available in Jest's setup files\n const testPath: string = (g.expect?.getState?.()?.testPath as string | undefined) ?? '';\n return buildTestId(testPath, [...describeStack, name]);\n}\n\n// Override describe to maintain the stack\nconst originalDescribe = g.describe as ((name: string, fn: () => void) => void) | undefined;\nif (originalDescribe) {\n g.describe = Object.assign(\n (name: string, fn: () => void) => {\n describeStack.push(name);\n originalDescribe(name, () => {\n try {\n fn();\n } finally {\n describeStack.pop();\n }\n });\n },\n originalDescribe,\n );\n}\n\n// Override test / it\ntype TestLike = ((name: string, fn?: () => void | Promise<void>, timeout?: number) => void) & {\n skip: (name: string, fn?: () => void | Promise<void>, timeout?: number) => void;\n [key: string]: unknown;\n};\n\nconst originalTest = g.test as TestLike | undefined;\nif (originalTest) {\n const wrapped = Object.assign(\n (name: string, fn?: () => void | Promise<void>, timeout?: number) => {\n if (fn === undefined) return originalTest(name, fn, timeout);\n const testId = buildCurrentTestId(name);\n appendDiscovered(testId);\n return resolver.isTestEnabled(testId)\n ? originalTest(name, fn, timeout)\n : originalTest.skip(name, fn, timeout);\n },\n originalTest,\n );\n\n g.test = wrapped;\n g.it = wrapped;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAWA,SAAoB;AACpB,WAAsB;AACtB,kBAA6C;AAG7C,IAAM,IAAI;AAEV,IAAM,gBAA0B,CAAC;AAEjC,SAAS,cAA+B;AACtC,QAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,QAAM,MAAS,gBAAa,WAAW,MAAM;AAC7C,SAAO,4BAAgB,SAAS,KAAK,MAAM,GAAG,CAAkC;AAClF;AAEA,IAAM,WAAW,YAAY;AAI7B,IAAM,gBAA0B,CAAC;AAEjC,SAAS,iBAAiB,QAAsB;AAC9C,gBAAc,KAAK,MAAM;AAC3B;AAIA,IAAM,gBAAgB,QAAQ,IAAI;AAClC,IAAI,eAAe;AACjB,QAAM,WAAW,QAAQ,IAAI,kBAAkB;AAC/C,QAAM,mBAAmB,EAAE;AAC3B,MAAI,kBAAkB;AACpB,qBAAiB,MAAM;AACrB,UAAI,cAAc,WAAW,EAAG;AAEhC,YAAM,SAAS,GAAG,QAAQ,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAC/E,MAAG;AAAA,QACI,UAAK,eAAe,GAAG,MAAM,OAAO;AAAA,QACzC,KAAK,UAAU,aAAa;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,SAAS,mBAAmB,MAAsB;AAEhD,QAAM,WAAoB,EAAE,QAAQ,WAAW,GAAG,YAAmC;AACrF,aAAO,yBAAY,UAAU,CAAC,GAAG,eAAe,IAAI,CAAC;AACvD;AAGA,IAAM,mBAAmB,EAAE;AAC3B,IAAI,kBAAkB;AACpB,IAAE,WAAW,OAAO;AAAA,IAClB,CAAC,MAAc,OAAmB;AAChC,oBAAc,KAAK,IAAI;AACvB,uBAAiB,MAAM,MAAM;AAC3B,YAAI;AACF,aAAG;AAAA,QACL,UAAE;AACA,wBAAc,IAAI;AAAA,QACpB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA;AAAA,EACF;AACF;AAQA,IAAM,eAAe,EAAE;AACvB,IAAI,cAAc;AAChB,QAAM,UAAU,OAAO;AAAA,IACrB,CAAC,MAAc,IAAiC,YAAqB;AACnE,UAAI,OAAO,OAAW,QAAO,aAAa,MAAM,IAAI,OAAO;AAC3D,YAAM,SAAS,mBAAmB,IAAI;AACtC,uBAAiB,MAAM;AACvB,aAAO,SAAS,cAAc,MAAM,IAChC,aAAa,MAAM,IAAI,OAAO,IAC9B,aAAa,KAAK,MAAM,IAAI,OAAO;AAAA,IACzC;AAAA,IACA;AAAA,EACF;AAEA,IAAE,OAAO;AACT,IAAE,KAAK;AACT;","names":[]}
|
package/dist/setup.mjs
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// src/setup.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { SkipperResolver, buildTestId } from "@get-skipper/core";
|
|
5
|
+
var g = global;
|
|
6
|
+
var describeStack = [];
|
|
7
|
+
function getResolver() {
|
|
8
|
+
const cacheFile = process.env.SKIPPER_CACHE_FILE;
|
|
9
|
+
if (!cacheFile) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
"[skipper] SKIPPER_CACHE_FILE is not set. Did you add createSkipperGlobalSetup() to globalSetup in jest.config?"
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
const raw = fs.readFileSync(cacheFile, "utf8");
|
|
15
|
+
return SkipperResolver.fromJSON(JSON.parse(raw));
|
|
16
|
+
}
|
|
17
|
+
var resolver = getResolver();
|
|
18
|
+
var discoveredIds = [];
|
|
19
|
+
function appendDiscovered(testId) {
|
|
20
|
+
discoveredIds.push(testId);
|
|
21
|
+
}
|
|
22
|
+
var discoveredDir = process.env.SKIPPER_DISCOVERED_DIR;
|
|
23
|
+
if (discoveredDir) {
|
|
24
|
+
const workerId = process.env.JEST_WORKER_ID ?? "0";
|
|
25
|
+
const originalAfterAll = g.afterAll;
|
|
26
|
+
if (originalAfterAll) {
|
|
27
|
+
originalAfterAll(() => {
|
|
28
|
+
if (discoveredIds.length === 0) return;
|
|
29
|
+
const suffix = `${workerId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
30
|
+
fs.writeFileSync(
|
|
31
|
+
path.join(discoveredDir, `${suffix}.json`),
|
|
32
|
+
JSON.stringify(discoveredIds)
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function buildCurrentTestId(name) {
|
|
38
|
+
const testPath = g.expect?.getState?.()?.testPath ?? "";
|
|
39
|
+
return buildTestId(testPath, [...describeStack, name]);
|
|
40
|
+
}
|
|
41
|
+
var originalDescribe = g.describe;
|
|
42
|
+
if (originalDescribe) {
|
|
43
|
+
g.describe = Object.assign(
|
|
44
|
+
(name, fn) => {
|
|
45
|
+
describeStack.push(name);
|
|
46
|
+
originalDescribe(name, () => {
|
|
47
|
+
try {
|
|
48
|
+
fn();
|
|
49
|
+
} finally {
|
|
50
|
+
describeStack.pop();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
originalDescribe
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
var originalTest = g.test;
|
|
58
|
+
if (originalTest) {
|
|
59
|
+
const wrapped = Object.assign(
|
|
60
|
+
(name, fn, timeout) => {
|
|
61
|
+
if (fn === void 0) return originalTest(name, fn, timeout);
|
|
62
|
+
const testId = buildCurrentTestId(name);
|
|
63
|
+
appendDiscovered(testId);
|
|
64
|
+
return resolver.isTestEnabled(testId) ? originalTest(name, fn, timeout) : originalTest.skip(name, fn, timeout);
|
|
65
|
+
},
|
|
66
|
+
originalTest
|
|
67
|
+
);
|
|
68
|
+
g.test = wrapped;
|
|
69
|
+
g.it = wrapped;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=setup.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/setup.ts"],"sourcesContent":["/**\n * Jest setup file — add to `setupFilesAfterEnv` in jest.config.ts.\n *\n * This file:\n * 1. Rehydrates the SkipperResolver from the cache file written by globalSetup\n * 2. Overrides global `test` and `it` to auto-skip disabled tests\n * 3. Collects all discovered test IDs and writes them to SKIPPER_DISCOVERED_DIR\n * after each test file, so globalTeardown can merge them across workers.\n *\n * Uses (global as any) casts to avoid depending on @types/jest.\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { SkipperResolver, buildTestId } from '@get-skipper/core';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst g = global as any;\n\nconst describeStack: string[] = [];\n\nfunction getResolver(): SkipperResolver {\n const cacheFile = process.env.SKIPPER_CACHE_FILE;\n if (!cacheFile) {\n throw new Error(\n '[skipper] SKIPPER_CACHE_FILE is not set. ' +\n 'Did you add createSkipperGlobalSetup() to globalSetup in jest.config?',\n );\n }\n const raw = fs.readFileSync(cacheFile, 'utf8');\n return SkipperResolver.fromJSON(JSON.parse(raw) as Record<string, string | null>);\n}\n\nconst resolver = getResolver();\n\n// Collect discovered test IDs in memory; flush to disk in afterAll so\n// globalTeardown (a separate process) can read them across all workers.\nconst discoveredIds: string[] = [];\n\nfunction appendDiscovered(testId: string): void {\n discoveredIds.push(testId);\n}\n\n// Register a root-level afterAll to flush this worker's discoveries to disk.\n// SKIPPER_DISCOVERED_DIR is set by globalSetup and propagated to all workers.\nconst discoveredDir = process.env.SKIPPER_DISCOVERED_DIR;\nif (discoveredDir) {\n const workerId = process.env.JEST_WORKER_ID ?? '0';\n const originalAfterAll = g.afterAll as ((fn: () => void) => void) | undefined;\n if (originalAfterAll) {\n originalAfterAll(() => {\n if (discoveredIds.length === 0) return;\n // Use worker ID + timestamp + random to guarantee a unique file name per test file.\n const suffix = `${workerId}-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n fs.writeFileSync(\n path.join(discoveredDir, `${suffix}.json`),\n JSON.stringify(discoveredIds),\n );\n });\n }\n}\n\nfunction buildCurrentTestId(name: string): string {\n // `expect.getState().testPath` is available in Jest's setup files\n const testPath: string = (g.expect?.getState?.()?.testPath as string | undefined) ?? '';\n return buildTestId(testPath, [...describeStack, name]);\n}\n\n// Override describe to maintain the stack\nconst originalDescribe = g.describe as ((name: string, fn: () => void) => void) | undefined;\nif (originalDescribe) {\n g.describe = Object.assign(\n (name: string, fn: () => void) => {\n describeStack.push(name);\n originalDescribe(name, () => {\n try {\n fn();\n } finally {\n describeStack.pop();\n }\n });\n },\n originalDescribe,\n );\n}\n\n// Override test / it\ntype TestLike = ((name: string, fn?: () => void | Promise<void>, timeout?: number) => void) & {\n skip: (name: string, fn?: () => void | Promise<void>, timeout?: number) => void;\n [key: string]: unknown;\n};\n\nconst originalTest = g.test as TestLike | undefined;\nif (originalTest) {\n const wrapped = Object.assign(\n (name: string, fn?: () => void | Promise<void>, timeout?: number) => {\n if (fn === undefined) return originalTest(name, fn, timeout);\n const testId = buildCurrentTestId(name);\n appendDiscovered(testId);\n return resolver.isTestEnabled(testId)\n ? originalTest(name, fn, timeout)\n : originalTest.skip(name, fn, timeout);\n },\n originalTest,\n );\n\n g.test = wrapped;\n g.it = wrapped;\n}\n"],"mappings":";AAWA,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,SAAS,iBAAiB,mBAAmB;AAG7C,IAAM,IAAI;AAEV,IAAM,gBAA0B,CAAC;AAEjC,SAAS,cAA+B;AACtC,QAAM,YAAY,QAAQ,IAAI;AAC9B,MAAI,CAAC,WAAW;AACd,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,QAAM,MAAS,gBAAa,WAAW,MAAM;AAC7C,SAAO,gBAAgB,SAAS,KAAK,MAAM,GAAG,CAAkC;AAClF;AAEA,IAAM,WAAW,YAAY;AAI7B,IAAM,gBAA0B,CAAC;AAEjC,SAAS,iBAAiB,QAAsB;AAC9C,gBAAc,KAAK,MAAM;AAC3B;AAIA,IAAM,gBAAgB,QAAQ,IAAI;AAClC,IAAI,eAAe;AACjB,QAAM,WAAW,QAAQ,IAAI,kBAAkB;AAC/C,QAAM,mBAAmB,EAAE;AAC3B,MAAI,kBAAkB;AACpB,qBAAiB,MAAM;AACrB,UAAI,cAAc,WAAW,EAAG;AAEhC,YAAM,SAAS,GAAG,QAAQ,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AAC/E,MAAG;AAAA,QACI,UAAK,eAAe,GAAG,MAAM,OAAO;AAAA,QACzC,KAAK,UAAU,aAAa;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,SAAS,mBAAmB,MAAsB;AAEhD,QAAM,WAAoB,EAAE,QAAQ,WAAW,GAAG,YAAmC;AACrF,SAAO,YAAY,UAAU,CAAC,GAAG,eAAe,IAAI,CAAC;AACvD;AAGA,IAAM,mBAAmB,EAAE;AAC3B,IAAI,kBAAkB;AACpB,IAAE,WAAW,OAAO;AAAA,IAClB,CAAC,MAAc,OAAmB;AAChC,oBAAc,KAAK,IAAI;AACvB,uBAAiB,MAAM,MAAM;AAC3B,YAAI;AACF,aAAG;AAAA,QACL,UAAE;AACA,wBAAc,IAAI;AAAA,QACpB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,IACA;AAAA,EACF;AACF;AAQA,IAAM,eAAe,EAAE;AACvB,IAAI,cAAc;AAChB,QAAM,UAAU,OAAO;AAAA,IACrB,CAAC,MAAc,IAAiC,YAAqB;AACnE,UAAI,OAAO,OAAW,QAAO,aAAa,MAAM,IAAI,OAAO;AAC3D,YAAM,SAAS,mBAAmB,IAAI;AACtC,uBAAiB,MAAM;AACvB,aAAO,SAAS,cAAc,MAAM,IAChC,aAAa,MAAM,IAAI,OAAO,IAC9B,aAAa,KAAK,MAAM,IAAI,OAAO;AAAA,IACzC;AAAA,IACA;AAAA,EACF;AAEA,IAAE,OAAO;AACT,IAAE,KAAK;AACT;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@get-skipper/jest",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Skipper plugin for Jest — enable/disable tests from a Google Spreadsheet",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"skipper",
|
|
7
|
+
"jest",
|
|
8
|
+
"testing",
|
|
9
|
+
"google-sheets",
|
|
10
|
+
"test-gating"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"main": "dist/index.js",
|
|
14
|
+
"module": "dist/index.mjs",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.mjs",
|
|
20
|
+
"require": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@get-skipper/core": "1.0.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"jest": ">=29.0.0"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "jest --config ../../jest.config.js --testPathPattern=packages/jest/"
|
|
42
|
+
}
|
|
43
|
+
}
|