@sdods/core 0.2.2 → 0.3.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.
Files changed (52) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/analyze/detectors.js +236 -24
  3. package/dist/analyze/index.d.ts +0 -1
  4. package/dist/analyze/index.js +0 -1
  5. package/dist/analyze/propose.js +80 -38
  6. package/dist/analyze/scan.js +19 -1
  7. package/dist/api/client.js +7 -1
  8. package/dist/auth/capture.js +19 -6
  9. package/dist/auth/index.js +23 -2
  10. package/dist/config/resolve.d.ts +13 -0
  11. package/dist/config/resolve.js +1 -0
  12. package/dist/config/tags.d.ts +29 -1
  13. package/dist/config/tags.js +46 -0
  14. package/dist/data/provider.js +5 -1
  15. package/dist/data/user-pool.js +35 -3
  16. package/dist/fixtures/api-context.d.ts +14 -1
  17. package/dist/fixtures/api-context.js +13 -0
  18. package/dist/fixtures/scenario.js +1 -4
  19. package/dist/fixtures/test.js +42 -1
  20. package/dist/fixtures/types.d.ts +2 -0
  21. package/dist/reporters/dashboard.d.ts +86 -0
  22. package/dist/reporters/dashboard.js +319 -61
  23. package/dist/shots/hooks.js +0 -10
  24. package/dist/steps/a11y.steps.d.ts +180 -0
  25. package/dist/steps/a11y.steps.js +598 -0
  26. package/dist/steps/api.steps.js +5 -1
  27. package/dist/steps/browser.steps.d.ts +27 -0
  28. package/dist/steps/browser.steps.js +653 -0
  29. package/dist/steps/clock.steps.d.ts +4 -0
  30. package/dist/steps/clock.steps.js +73 -0
  31. package/dist/steps/data.steps.js +50 -2
  32. package/dist/steps/db.steps.d.ts +5 -0
  33. package/dist/steps/db.steps.js +105 -0
  34. package/dist/steps/dom.steps.d.ts +2 -0
  35. package/dist/steps/dom.steps.js +583 -0
  36. package/dist/steps/iframe.steps.d.ts +2 -0
  37. package/dist/steps/iframe.steps.js +93 -0
  38. package/dist/steps/index.d.ts +10 -0
  39. package/dist/steps/index.js +10 -0
  40. package/dist/steps/net.steps.d.ts +63 -0
  41. package/dist/steps/net.steps.js +728 -0
  42. package/dist/steps/perf.steps.d.ts +248 -0
  43. package/dist/steps/perf.steps.js +514 -0
  44. package/dist/steps/tabs.steps.d.ts +5 -0
  45. package/dist/steps/tabs.steps.js +109 -0
  46. package/dist/steps/webhook.steps.d.ts +46 -0
  47. package/dist/steps/webhook.steps.js +129 -0
  48. package/package.json +3 -4
  49. package/dist/analyze/modules.d.ts +0 -74
  50. package/dist/analyze/modules.js +0 -353
  51. package/dist/config/playwright.d.ts +0 -37
  52. package/dist/config/playwright.js +0 -262
@@ -1,262 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { join, resolve as resolvePath } from 'node:path';
3
- import { devices } from '@playwright/test';
4
- import { cucumberReporter, defineBddConfig } from 'playwright-bdd';
5
- import { pwProjectName, runFiles } from '@sdods/contracts';
6
- import { coreStepsGlob } from '../steps/glob.js';
7
- import { combineTagExpr, normalizeTagExpr } from './tags.js';
8
- const DEVICE_FOR_BROWSER = {
9
- chromium: 'Desktop Chrome',
10
- firefox: 'Desktop Firefox',
11
- webkit: 'Desktop Safari',
12
- 'mobile-chrome': 'Pixel 7',
13
- 'mobile-safari': 'iPhone 15',
14
- };
15
- export const DASHBOARD_REPORTER = '@sdods/core/reporters/dashboard';
16
- /** Convenience for `playwright.config.ts`: read the selection from SDODS_* env vars. */
17
- export function selectionFromEnv(env = process.env) {
18
- const list = (v) => v
19
- ? v
20
- .split(',')
21
- .map((s) => s.trim())
22
- .filter(Boolean)
23
- : undefined;
24
- return {
25
- project: env.SDODS_PROJECT || undefined,
26
- env: env.SDODS_ENV || undefined,
27
- layers: list(env.SDODS_LAYERS),
28
- browsers: list(env.SDODS_BROWSERS),
29
- tags: normalizeTagExpr(env.SDODS_TAGS),
30
- runId: env.SDODS_RUN_ID || undefined,
31
- lint: env.SDODS_LINT === '1',
32
- allure: env.SDODS_ALLURE === '1',
33
- reporters: list(env.SDODS_REPORTERS),
34
- reporterMode: env.SDODS_REPORTER_MODE || 'default',
35
- };
36
- }
37
- /** Names (and identity) of the Playwright projects a selection would produce, without side effects. */
38
- export function listGeneratedProjects(registry, sel) {
39
- const out = [];
40
- for (const entry of sel.project ? [registry.entry(sel.project)] : registry.entriesList()) {
41
- const p = entry.config;
42
- const layers = (sel.layers?.length ? p.layers.filter((l) => sel.layers.includes(l)) : p.layers);
43
- const browsers = (sel.browsers?.length
44
- ? p.browsers.filter((b) => sel.browsers.includes(b))
45
- : p.browsers);
46
- for (const layer of layers) {
47
- if (layer === 'api') {
48
- out.push({ name: pwProjectName({ project: p.slug, layer }), project: p.slug, layer });
49
- continue;
50
- }
51
- if (layer === 'recorded' && !existsSync(join(entry.root, 'recorded')))
52
- continue;
53
- for (const browser of browsers) {
54
- out.push({
55
- name: pwProjectName({ project: p.slug, layer, browser }),
56
- project: p.slug,
57
- layer,
58
- browser,
59
- });
60
- }
61
- }
62
- }
63
- return out;
64
- }
65
- /**
66
- * Build the Playwright config for a selection of projects × layers × browsers.
67
- * One `defineBddConfig` per project × layer; browsers reuse the generated testDir.
68
- */
69
- export function buildPlaywrightConfig(registry, sel = {}) {
70
- const entries = sel.project ? [registry.entry(sel.project)] : registry.entriesList();
71
- const projects = [];
72
- let first;
73
- let bddConfigs = 0;
74
- const cliOverrides = sel.runId ? { runId: sel.runId } : undefined;
75
- for (const entry of entries) {
76
- const cfg = registry.resolve(entry.slug, sel.env, cliOverrides ? { ...parseEnvOverrides(), ...cliOverrides } : undefined);
77
- first ??= cfg;
78
- const p = cfg.project;
79
- const layers = (sel.layers?.length ? p.layers.filter((l) => sel.layers.includes(l)) : p.layers);
80
- const browsers = (sel.browsers?.length
81
- ? p.browsers.filter((b) => sel.browsers.includes(b))
82
- : p.browsers);
83
- const envUse = {
84
- locale: cfg.env.use.locale,
85
- timezoneId: cfg.env.use.timezoneId,
86
- geolocation: cfg.env.use.geolocation,
87
- permissions: cfg.env.use.permissions,
88
- colorScheme: cfg.env.use.colorScheme,
89
- ignoreHTTPSErrors: cfg.env.use.ignoreHTTPSErrors,
90
- extraHTTPHeaders: cfg.env.use.extraHTTPHeaders,
91
- httpCredentials: cfg.env.use.httpCredentials,
92
- };
93
- for (const k of Object.keys(envUse))
94
- if (envUse[k] === undefined)
95
- delete envUse[k];
96
- for (const layer of layers) {
97
- if (layer === 'recorded') {
98
- const recordedDir = join(p.root, 'recorded');
99
- if (!existsSync(recordedDir))
100
- continue;
101
- for (const browser of browsers) {
102
- projects.push({
103
- name: pwProjectName({ project: p.slug, layer, browser }),
104
- testDir: recordedDir,
105
- testMatch: '**/*.spec.ts',
106
- snapshotPathTemplate: join(p.root, 'features', '__screenshots__', '{projectName}', '{platform}', '{arg}{ext}'),
107
- use: {
108
- ...devices[DEVICE_FOR_BROWSER[browser]],
109
- ...(p.channel && (browser === 'chromium' || browser === 'mobile-chrome')
110
- ? { channel: p.channel }
111
- : {}),
112
- baseURL: cfg.env.ui.baseUrl,
113
- testIdAttribute: p.testIdAttribute,
114
- ...envUse,
115
- sdods: { project: p.slug, layer, browser },
116
- },
117
- });
118
- }
119
- continue;
120
- }
121
- const testDir = defineBddConfig({
122
- features: `${toPosix(p.root)}/features/**/*.feature`,
123
- steps: [
124
- coreStepsGlob(),
125
- `${toPosix(p.root)}/steps/**/*.ts`,
126
- `${toPosix(p.root)}/pages/**/*.ts`,
127
- ],
128
- // Each run generates into its own dir (cleaned by `sdods run`), lint/export into `.lint`,
129
- // so concurrent runs and tooling never race on generated specs.
130
- outputDir: `${toPosix(join(cfg.runtime.repoRoot, '.features-gen', sel.lint ? '.lint' : (sel.runId ?? 'adhoc'), p.slug, layer))}`,
131
- featuresRoot: `${toPosix(p.root)}/features`,
132
- // Explicit: scenarios that only use core steps cannot let bddgen guess the project test instance.
133
- importTestFrom: `${toPosix(p.root)}/steps/fixtures.ts`,
134
- disableWarnings: { importTestFrom: true },
135
- tags: combineTagExpr(`@${layer}`, sel.tags),
136
- examplesTitleFormat: 'Example #<_index_>',
137
- missingSteps: sel.lint ? 'fail-on-gen' : 'fail-on-run',
138
- aiFix: { promptAttachment: true },
139
- quotes: 'single',
140
- });
141
- bddConfigs++;
142
- if (layer === 'api') {
143
- projects.push({
144
- name: pwProjectName({ project: p.slug, layer }),
145
- testDir,
146
- use: { sdods: { project: p.slug, layer } },
147
- });
148
- continue;
149
- }
150
- for (const browser of browsers) {
151
- projects.push({
152
- name: pwProjectName({ project: p.slug, layer, browser }),
153
- testDir,
154
- // Baselines live with the project (generated specs are per-run and deleted):
155
- // projects/<slug>/features/__screenshots__/<pw project>/<platform>/<name>.png
156
- snapshotPathTemplate: join(p.root, 'features', '__screenshots__', '{projectName}', '{platform}', '{arg}{ext}'),
157
- use: {
158
- ...devices[DEVICE_FOR_BROWSER[browser]],
159
- ...(p.channel && (browser === 'chromium' || browser === 'mobile-chrome')
160
- ? { channel: p.channel }
161
- : {}),
162
- baseURL: cfg.env.ui.baseUrl,
163
- testIdAttribute: p.testIdAttribute,
164
- viewport: browser.startsWith('mobile') ? undefined : p.screenshots.viewport,
165
- ...envUse,
166
- sdods: { project: p.slug, layer, browser },
167
- },
168
- });
169
- }
170
- }
171
- }
172
- const runDir = first?.runtime.runDir ?? resolvePath('.sdods/runs/adhoc');
173
- const timeouts = first?.project.timeouts ?? {
174
- test: 60_000,
175
- expect: 10_000,
176
- action: 15_000,
177
- navigation: 30_000,
178
- api: 15_000,
179
- };
180
- const workers = first?.runtime.workers;
181
- const retries = first?.runtime.retries ?? 0;
182
- const reporterMode = sel.reporterMode ?? 'default';
183
- const reporter = [];
184
- // `--reporter <name[=outputFile]>` ADDS reporters (e.g. `blob` for sharded CI) to the SDODS
185
- // defaults, so the NDJSON, dashboard and HTML report always exist. Path-less `blob`/`json`/`junit`
186
- // land inside the run directory.
187
- const extra = (sel.reporters ?? []).map((r) => {
188
- const [name, file] = r.split('=');
189
- if (file)
190
- return [name, name === 'blob' ? { outputDir: file } : { outputFile: file }];
191
- if (name === 'blob')
192
- return ['blob', { outputDir: join(runDir, 'blob-report') }];
193
- if (name === 'json')
194
- return ['json', { outputFile: join(runDir, 'pw-results.extra.json') }];
195
- if (name === 'junit')
196
- return ['junit', { outputFile: join(runDir, runFiles.junit) }];
197
- return [name];
198
- });
199
- {
200
- reporter.push(reporterMode === 'server' ? ['line'] : reporterMode === 'quiet' ? ['dot'] : ['list']);
201
- reporter.push(['html', { outputFolder: join(runDir, runFiles.pwReport), open: 'never' }]);
202
- // The cucumber reporter throws when no defineBddConfig() ran (recorded-only selections).
203
- if (bddConfigs > 0)
204
- reporter.push(cucumberReporter('message', {
205
- outputFile: join(runDir, runFiles.messages),
206
- }));
207
- reporter.push([DASHBOARD_REPORTER, { outputDir: join(runDir, runFiles.dashboard) }]);
208
- if (projects.some((p) => String(p.name).includes('--recorded--'))) {
209
- reporter.push(['json', { outputFile: join(runDir, runFiles.pwResults) }]);
210
- }
211
- if (first?.project.reports.junit || first?.runtime.ci)
212
- reporter.push(['junit', { outputFile: join(runDir, runFiles.junit) }]);
213
- if (first?.project.reports.cucumberHtml)
214
- reporter.push(cucumberReporter('html', {
215
- outputFile: join(runDir, 'cucumber-report.html'),
216
- }));
217
- if (sel.allure || first?.project.reports.allure)
218
- reporter.push(['allure-playwright', { resultsDir: join(runDir, 'allure-results') }]);
219
- }
220
- for (const r of extra) {
221
- if (reporter.some((d) => d[0] === r[0]))
222
- continue; // already emitted by defaults (e.g. junit in CI)
223
- reporter.push(r);
224
- }
225
- return {
226
- timeout: timeouts.test,
227
- expect: { timeout: timeouts.expect },
228
- retries,
229
- workers,
230
- fullyParallel: true,
231
- outputDir: join(runDir, runFiles.pwOutput),
232
- reporter,
233
- use: {
234
- screenshot: 'off',
235
- video: 'retain-on-failure',
236
- trace: 'on-first-retry',
237
- actionTimeout: timeouts.action,
238
- navigationTimeout: timeouts.navigation,
239
- },
240
- projects,
241
- metadata: {
242
- sdodsRunId: first?.runtime.runId,
243
- sdodsEnv: first?.env.name,
244
- sdodsRunDir: runDir,
245
- },
246
- };
247
- }
248
- function parseEnvOverrides() {
249
- const raw = process.env.SDODS_CLI_OVERRIDES;
250
- if (!raw)
251
- return {};
252
- try {
253
- return JSON.parse(raw);
254
- }
255
- catch {
256
- return {};
257
- }
258
- }
259
- function toPosix(p) {
260
- return p.replace(/\\/g, '/');
261
- }
262
- //# sourceMappingURL=playwright.js.map