@regardio/dev 0.9.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 (75) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +416 -0
  3. package/dist/bin/exec-clean.d.ts +3 -0
  4. package/dist/bin/exec-clean.d.ts.map +1 -0
  5. package/dist/bin/exec-clean.js +18 -0
  6. package/dist/bin/exec-husky.d.ts +3 -0
  7. package/dist/bin/exec-husky.d.ts.map +1 -0
  8. package/dist/bin/exec-husky.js +8 -0
  9. package/dist/bin/exec-p.d.ts +3 -0
  10. package/dist/bin/exec-p.d.ts.map +1 -0
  11. package/dist/bin/exec-p.js +8 -0
  12. package/dist/bin/exec-s.d.ts +3 -0
  13. package/dist/bin/exec-s.d.ts.map +1 -0
  14. package/dist/bin/exec-s.js +8 -0
  15. package/dist/bin/exec-ts.d.ts +3 -0
  16. package/dist/bin/exec-ts.d.ts.map +1 -0
  17. package/dist/bin/exec-ts.js +28 -0
  18. package/dist/bin/exec-tsc.d.ts +3 -0
  19. package/dist/bin/exec-tsc.d.ts.map +1 -0
  20. package/dist/bin/exec-tsc.js +8 -0
  21. package/dist/bin/flow-branch.d.ts +3 -0
  22. package/dist/bin/flow-branch.d.ts.map +1 -0
  23. package/dist/bin/flow-branch.js +27 -0
  24. package/dist/bin/flow-version.d.ts +3 -0
  25. package/dist/bin/flow-version.d.ts.map +1 -0
  26. package/dist/bin/flow-version.js +83 -0
  27. package/dist/bin/lint-biome.d.ts +3 -0
  28. package/dist/bin/lint-biome.d.ts.map +1 -0
  29. package/dist/bin/lint-biome.js +8 -0
  30. package/dist/bin/lint-commit.d.ts +3 -0
  31. package/dist/bin/lint-commit.d.ts.map +1 -0
  32. package/dist/bin/lint-commit.js +8 -0
  33. package/dist/bin/lint-md.d.ts +3 -0
  34. package/dist/bin/lint-md.d.ts.map +1 -0
  35. package/dist/bin/lint-md.js +10 -0
  36. package/dist/config.test.d.ts +2 -0
  37. package/dist/config.test.d.ts.map +1 -0
  38. package/dist/config.test.js +101 -0
  39. package/dist/playwright/index.d.ts +10 -0
  40. package/dist/playwright/index.d.ts.map +1 -0
  41. package/dist/playwright/index.js +42 -0
  42. package/dist/playwright/index.test.d.ts +2 -0
  43. package/dist/playwright/index.test.d.ts.map +1 -0
  44. package/dist/playwright/index.test.js +55 -0
  45. package/dist/testing/setup-react.d.ts +2 -0
  46. package/dist/testing/setup-react.d.ts.map +1 -0
  47. package/dist/testing/setup-react.js +1 -0
  48. package/dist/vitest/node.d.ts +3 -0
  49. package/dist/vitest/node.d.ts.map +1 -0
  50. package/dist/vitest/node.js +6 -0
  51. package/dist/vitest/react.d.ts +3 -0
  52. package/dist/vitest/react.d.ts.map +1 -0
  53. package/dist/vitest/react.js +7 -0
  54. package/package.json +104 -0
  55. package/src/bin/exec-clean.ts +24 -0
  56. package/src/bin/exec-husky.ts +13 -0
  57. package/src/bin/exec-p.ts +13 -0
  58. package/src/bin/exec-s.ts +13 -0
  59. package/src/bin/exec-ts.ts +39 -0
  60. package/src/bin/exec-tsc.ts +13 -0
  61. package/src/bin/lint-biome.ts +13 -0
  62. package/src/bin/lint-commit.ts +13 -0
  63. package/src/bin/lint-md.ts +16 -0
  64. package/src/biome/preset.json +297 -0
  65. package/src/commitlint/commitlint.cjs +26 -0
  66. package/src/config.test.ts +129 -0
  67. package/src/markdownlint/markdownlint.json +9 -0
  68. package/src/playwright/index.test.ts +73 -0
  69. package/src/playwright/index.ts +63 -0
  70. package/src/testing/setup-react.ts +8 -0
  71. package/src/typescript/base.json +50 -0
  72. package/src/typescript/build.json +19 -0
  73. package/src/typescript/react.json +8 -0
  74. package/src/vitest/node.ts +12 -0
  75. package/src/vitest/react.ts +15 -0
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint-biome: Run Biome (formatter/linter) against the codebase.
4
+ * Usage: lint-biome [biome args...]
5
+ */
6
+ import { spawn } from 'node:child_process';
7
+ import { createRequire } from 'node:module';
8
+
9
+ const require = createRequire(import.meta.url);
10
+ const bin = require.resolve('@biomejs/biome/bin/biome');
11
+ const args = process.argv.slice(2);
12
+ const child = spawn(process.execPath, [bin, ...args], { stdio: 'inherit' });
13
+ child.on('exit', (code) => process.exit(code ?? 0));
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint-commit: Run commitlint against commit messages.
4
+ * Usage: lint-commit [commitlint args...]
5
+ */
6
+ import { spawn } from 'node:child_process';
7
+ import { createRequire } from 'node:module';
8
+
9
+ const require = createRequire(import.meta.url);
10
+ const bin = require.resolve('@commitlint/cli/cli.js');
11
+ const args = process.argv.slice(2);
12
+ const child = spawn(process.execPath, [bin, ...args], { stdio: 'inherit' });
13
+ child.on('exit', (code) => process.exit(code ?? 0));
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint-md: Run markdownlint-cli2 to lint and fix Markdown files.
4
+ * Usage: lint-md [markdownlint-cli2 args...]
5
+ */
6
+ import { spawn } from 'node:child_process';
7
+ import { createRequire } from 'node:module';
8
+ import { dirname, join } from 'node:path';
9
+
10
+ const require = createRequire(import.meta.url);
11
+ // Resolve to any exported file to find the package directory
12
+ const packageDir = dirname(require.resolve('markdownlint-cli2'));
13
+ const bin = join(packageDir, 'markdownlint-cli2-bin.mjs');
14
+ const args = process.argv.slice(2);
15
+ const child = spawn(process.execPath, [bin, ...args], { stdio: 'inherit' });
16
+ child.on('exit', (code) => process.exit(code ?? 0));
@@ -0,0 +1,297 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/latest/schema.json",
3
+ "assist": {
4
+ "actions": {
5
+ "source": {
6
+ "organizeImports": "on",
7
+ "useSortedAttributes": "on",
8
+ "useSortedKeys": "on",
9
+ "useSortedProperties": "on"
10
+ }
11
+ }
12
+ },
13
+ "css": {
14
+ "assist": {
15
+ "enabled": true
16
+ },
17
+ "formatter": {
18
+ "enabled": true,
19
+ "indentStyle": "space",
20
+ "indentWidth": 2,
21
+ "lineEnding": "lf",
22
+ "lineWidth": 100,
23
+ "quoteStyle": "double"
24
+ },
25
+ "linter": {
26
+ "enabled": true
27
+ },
28
+ "parser": {
29
+ "cssModules": true,
30
+ "tailwindDirectives": true
31
+ }
32
+ },
33
+ "files": {
34
+ "ignoreUnknown": false,
35
+ "maxSize": 1048576
36
+ },
37
+ "formatter": {
38
+ "attributePosition": "multiline",
39
+ "bracketSpacing": true,
40
+ "enabled": true,
41
+ "expand": "auto",
42
+ "formatWithErrors": true,
43
+ "indentStyle": "space",
44
+ "indentWidth": 2,
45
+ "lineEnding": "lf",
46
+ "lineWidth": 100,
47
+ "useEditorconfig": true
48
+ },
49
+ "graphql": {
50
+ "assist": {
51
+ "enabled": true
52
+ },
53
+ "formatter": {
54
+ "enabled": false,
55
+ "indentStyle": "space",
56
+ "indentWidth": 2,
57
+ "lineEnding": "lf",
58
+ "lineWidth": 100,
59
+ "quoteStyle": "double"
60
+ },
61
+ "linter": {
62
+ "enabled": true
63
+ }
64
+ },
65
+ "html": {
66
+ "assist": {
67
+ "enabled": true
68
+ },
69
+ "experimentalFullSupportEnabled": true,
70
+ "formatter": {
71
+ "attributePosition": "multiline",
72
+ "bracketSameLine": false,
73
+ "enabled": true,
74
+ "indentScriptAndStyle": true,
75
+ "indentStyle": "space",
76
+ "indentWidth": 2,
77
+ "lineEnding": "lf",
78
+ "lineWidth": 100
79
+ },
80
+ "linter": {
81
+ "enabled": true
82
+ }
83
+ },
84
+ "javascript": {
85
+ "assist": {
86
+ "enabled": true
87
+ },
88
+ "formatter": {
89
+ "arrowParentheses": "always",
90
+ "attributePosition": "multiline",
91
+ "bracketSameLine": false,
92
+ "bracketSpacing": true,
93
+ "enabled": true,
94
+ "expand": "auto",
95
+ "indentStyle": "space",
96
+ "indentWidth": 2,
97
+ "jsxQuoteStyle": "double",
98
+ "lineEnding": "lf",
99
+ "lineWidth": 100,
100
+ "quoteProperties": "asNeeded",
101
+ "quoteStyle": "single",
102
+ "semicolons": "always",
103
+ "trailingCommas": "all"
104
+ },
105
+ "globals": [],
106
+ "jsxRuntime": "transparent",
107
+ "linter": {
108
+ "enabled": true
109
+ },
110
+ "parser": {
111
+ "jsxEverywhere": true,
112
+ "unsafeParameterDecoratorsEnabled": false
113
+ }
114
+ },
115
+ "json": {
116
+ "assist": {
117
+ "enabled": true
118
+ },
119
+ "formatter": {
120
+ "bracketSpacing": true,
121
+ "enabled": true,
122
+ "expand": "auto",
123
+ "indentStyle": "space",
124
+ "indentWidth": 2,
125
+ "lineEnding": "lf",
126
+ "lineWidth": 100,
127
+ "trailingCommas": "none"
128
+ },
129
+ "linter": {
130
+ "enabled": true
131
+ },
132
+ "parser": {
133
+ "allowComments": true,
134
+ "allowTrailingCommas": true
135
+ }
136
+ },
137
+ "linter": {
138
+ "enabled": true,
139
+ "rules": {
140
+ "a11y": {
141
+ "noSvgWithoutTitle": "off",
142
+ "recommended": true,
143
+ "useGenericFontNames": "error"
144
+ },
145
+ "complexity": {
146
+ "noArguments": "error",
147
+ "noCommaOperator": "error",
148
+ "recommended": true,
149
+ "useNumericLiterals": "error"
150
+ },
151
+ "correctness": {
152
+ "noInvalidPositionAtImportRule": "off",
153
+ "noUnknownFunction": "off",
154
+ "noUnknownTypeSelector": "off",
155
+ "recommended": true,
156
+ "useUniqueElementIds": {
157
+ "level": "error",
158
+ "options": {
159
+ "excludedComponents": [
160
+ "circle",
161
+ "clipPath",
162
+ "defs",
163
+ "ellipse",
164
+ "filter",
165
+ "g",
166
+ "input",
167
+ "line",
168
+ "linearGradient",
169
+ "mask",
170
+ "path",
171
+ "pattern",
172
+ "polygon",
173
+ "polyline",
174
+ "radialGradient",
175
+ "rect",
176
+ "stop",
177
+ "svg",
178
+ "symbol",
179
+ "Trans"
180
+ ]
181
+ }
182
+ },
183
+ "useValidTypeof": "error"
184
+ },
185
+ "nursery": {
186
+ "recommended": true
187
+ },
188
+ "performance": {
189
+ "noBarrelFile": "error",
190
+ "recommended": true
191
+ },
192
+ "recommended": true,
193
+ "security": {
194
+ "noDangerouslySetInnerHtml": "off",
195
+ "recommended": true
196
+ },
197
+ "style": {
198
+ "noInferrableTypes": "error",
199
+ "noNonNullAssertion": "error",
200
+ "noParameterAssign": "error",
201
+ "noUnusedTemplateLiteral": "error",
202
+ "noUselessElse": "error",
203
+ "recommended": true,
204
+ "useAsConstAssertion": "error",
205
+ "useConst": "error",
206
+ "useDefaultParameterLast": "error",
207
+ "useEnumInitializers": "error",
208
+ "useExponentiationOperator": "error",
209
+ "useExportType": "error",
210
+ "useImportType": "error",
211
+ "useLiteralEnumMembers": "error",
212
+ "useNodejsImportProtocol": "error",
213
+ "useNumberNamespace": "error",
214
+ "useSelfClosingElements": "error",
215
+ "useShorthandFunctionType": "error",
216
+ "useSingleVarDeclarator": "error",
217
+ "useTemplate": "error"
218
+ },
219
+ "suspicious": {
220
+ "noApproximativeNumericConstant": "error",
221
+ "noArrayIndexKey": "error",
222
+ "noAssignInExpressions": "error",
223
+ "noAsyncPromiseExecutor": "error",
224
+ "noCatchAssign": "error",
225
+ "noClassAssign": "error",
226
+ "noCommentText": "error",
227
+ "noCompareNegZero": "error",
228
+ "noConfusingLabels": "error",
229
+ "noConfusingVoidType": "error",
230
+ "noConsole": "off",
231
+ "noConstEnum": "error",
232
+ "noControlCharactersInRegex": "error",
233
+ "noDebugger": "error",
234
+ "noDoubleEquals": "error",
235
+ "noDuplicateAtImportRules": "error",
236
+ "noDuplicateCase": "error",
237
+ "noDuplicateClassMembers": "error",
238
+ "noDuplicateFontNames": "error",
239
+ "noDuplicateJsxProps": "error",
240
+ "noDuplicateObjectKeys": "error",
241
+ "noDuplicateParameters": "error",
242
+ "noDuplicateSelectorsKeyframeBlock": "error",
243
+ "noDuplicateTestHooks": "error",
244
+ "noEmptyBlock": "error",
245
+ "noEmptyBlockStatements": "off",
246
+ "noEmptyInterface": "error",
247
+ "noEvolvingTypes": "error",
248
+ "noExplicitAny": "error",
249
+ "noExportsInTest": "error",
250
+ "noExtraNonNullAssertion": "error",
251
+ "noFallthroughSwitchClause": "error",
252
+ "noFocusedTests": "error",
253
+ "noFunctionAssign": "error",
254
+ "noGlobalAssign": "error",
255
+ "noGlobalIsFinite": "error",
256
+ "noGlobalIsNan": "error",
257
+ "noImplicitAnyLet": "error",
258
+ "noImportAssign": "error",
259
+ "noImportantInKeyframe": "error",
260
+ "noLabelVar": "error",
261
+ "noMisleadingCharacterClass": "error",
262
+ "noMisleadingInstantiator": "error",
263
+ "noMisplacedAssertion": "error",
264
+ "noMisrefactoredShorthandAssign": "error",
265
+ "noPrototypeBuiltins": "error",
266
+ "noReactSpecificProps": "off",
267
+ "noRedeclare": "error",
268
+ "noRedundantUseStrict": "error",
269
+ "noSelfCompare": "error",
270
+ "noShadowRestrictedNames": "error",
271
+ "noShorthandPropertyOverrides": "error",
272
+ "noSkippedTests": "error",
273
+ "noSparseArray": "error",
274
+ "noSuspiciousSemicolonInJsx": "error",
275
+ "noThenProperty": "error",
276
+ "noUnknownAtRules": "off",
277
+ "noUnsafeDeclarationMerging": "error",
278
+ "noUnsafeNegation": "error",
279
+ "recommended": true,
280
+ "useAwait": "error",
281
+ "useDefaultSwitchClauseLast": "error",
282
+ "useErrorMessage": "error",
283
+ "useGetterReturn": "error",
284
+ "useIsArray": "error",
285
+ "useNamespaceKeyword": "error",
286
+ "useNumberToFixedDigitsArgument": "error"
287
+ }
288
+ }
289
+ },
290
+ "root": true,
291
+ "vcs": {
292
+ "clientKind": "git",
293
+ "defaultBranch": "main",
294
+ "enabled": true,
295
+ "useIgnoreFile": true
296
+ }
297
+ }
@@ -0,0 +1,26 @@
1
+ module.exports = {
2
+ extends: ['@commitlint/config-conventional'],
3
+ rules: {
4
+ 'body-leading-blank': [1, 'always'],
5
+ 'footer-leading-blank': [1, 'always'],
6
+ 'header-max-length': [2, 'always', 100],
7
+ 'type-enum': [
8
+ 2,
9
+ 'always',
10
+ [
11
+ 'BREAKING CHANGE',
12
+ 'build',
13
+ 'chore',
14
+ 'ci',
15
+ 'docs',
16
+ 'feat',
17
+ 'fix',
18
+ 'perf',
19
+ 'refactor',
20
+ 'revert',
21
+ 'style',
22
+ 'test',
23
+ ],
24
+ ],
25
+ },
26
+ };
@@ -0,0 +1,129 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { describe, expect, it } from 'vitest';
5
+
6
+ const srcDir = path.dirname(fileURLToPath(import.meta.url));
7
+
8
+ function readJson(filePath: string) {
9
+ // biome-ignore lint/suspicious/noExplicitAny: Test file needs flexible typing for nested config access
10
+ return JSON.parse(fs.readFileSync(filePath, 'utf8')) as any;
11
+ }
12
+
13
+ describe('Config Structure Validation', () => {
14
+ describe('biome/preset.json', () => {
15
+ const configPath = path.join(srcDir, 'biome/preset.json');
16
+ const config = readJson(configPath);
17
+
18
+ it('has $schema defined', () => {
19
+ expect(config.$schema).toContain('biomejs.dev');
20
+ });
21
+
22
+ it('has linter enabled', () => {
23
+ expect(config.linter?.enabled).toBe(true);
24
+ });
25
+
26
+ it('has formatter enabled', () => {
27
+ expect(config.formatter?.enabled).toBe(true);
28
+ });
29
+
30
+ it('has recommended rules enabled', () => {
31
+ expect(config.linter?.rules?.recommended).toBe(true);
32
+ });
33
+
34
+ it('disallows explicit any', () => {
35
+ expect(config.linter?.rules?.suspicious?.noExplicitAny).toBe('error');
36
+ });
37
+ });
38
+
39
+ describe('typescript/base.json', () => {
40
+ const configPath = path.join(srcDir, 'typescript/base.json');
41
+ const config = readJson(configPath);
42
+
43
+ it('has $schema defined', () => {
44
+ expect(config.$schema).toContain('schemastore.org');
45
+ });
46
+
47
+ it('has strict mode enabled', () => {
48
+ expect(config.compilerOptions?.strict).toBe(true);
49
+ });
50
+
51
+ it('has noUncheckedIndexedAccess enabled', () => {
52
+ expect(config.compilerOptions?.noUncheckedIndexedAccess).toBe(true);
53
+ });
54
+
55
+ it('has strictNullChecks enabled', () => {
56
+ expect(config.compilerOptions?.strictNullChecks).toBe(true);
57
+ });
58
+
59
+ it('has noImplicitAny enabled', () => {
60
+ expect(config.compilerOptions?.noImplicitAny).toBe(true);
61
+ });
62
+
63
+ it('targets ES2022', () => {
64
+ expect(config.compilerOptions?.target).toBe('ES2022');
65
+ });
66
+
67
+ it('uses ESNext module', () => {
68
+ expect(config.compilerOptions?.module).toBe('ESNext');
69
+ });
70
+ });
71
+
72
+ describe('typescript/react.json', () => {
73
+ const configPath = path.join(srcDir, 'typescript/react.json');
74
+ const config = readJson(configPath);
75
+
76
+ it('extends base.json', () => {
77
+ expect(config.extends).toContain('./base.json');
78
+ });
79
+
80
+ it('has jsx set to react-jsx', () => {
81
+ expect(config.compilerOptions?.jsx).toBe('react-jsx');
82
+ });
83
+ });
84
+
85
+ describe('markdownlint/markdownlint.json', () => {
86
+ const configPath = path.join(srcDir, 'markdownlint/markdownlint.json');
87
+ const config = readJson(configPath);
88
+
89
+ it('is a valid JSON object', () => {
90
+ expect(typeof config).toBe('object');
91
+ expect(config).not.toBeNull();
92
+ });
93
+
94
+ it('has MD013 (line-length) rule configured', () => {
95
+ expect(config.MD013).toBeDefined();
96
+ });
97
+ });
98
+
99
+ describe('commitlint/commitlint.cjs', () => {
100
+ const configPath = path.join(srcDir, 'commitlint/commitlint.cjs');
101
+
102
+ it('file exists', () => {
103
+ expect(fs.existsSync(configPath)).toBe(true);
104
+ });
105
+
106
+ it('exports extends with conventional config', () => {
107
+ const content = fs.readFileSync(configPath, 'utf8');
108
+ expect(content).toContain('@commitlint/config-conventional');
109
+ });
110
+ });
111
+ });
112
+
113
+ describe('Vitest Configs', () => {
114
+ describe('vitest/node', () => {
115
+ it('exports vitestNodeConfig', async () => {
116
+ const { vitestNodeConfig } = await import('./vitest/node.js');
117
+ expect(vitestNodeConfig).toBeDefined();
118
+ expect(vitestNodeConfig.environment).toBe('node');
119
+ });
120
+ });
121
+
122
+ describe('vitest/react', () => {
123
+ it('exports vitestReactConfig', async () => {
124
+ const { vitestReactConfig } = await import('./vitest/react.js');
125
+ expect(vitestReactConfig).toBeDefined();
126
+ expect(vitestReactConfig.environment).toBe('jsdom');
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "default": true,
3
+ "MD013": false,
4
+ "MD024": {
5
+ "siblings_only": false
6
+ },
7
+ "MD026": false,
8
+ "MD060": false
9
+ }
@@ -0,0 +1,73 @@
1
+ import { devices } from '@playwright/test';
2
+ import { describe, expect, it } from 'vitest';
3
+ import type { BuildPlaywrightBaseConfigParams } from './index.js';
4
+ import { buildPlaywrightBaseConfig } from './index.js';
5
+
6
+ describe('buildPlaywrightBaseConfig', () => {
7
+ const validParams: BuildPlaywrightBaseConfigParams = {
8
+ appPort: 3000,
9
+ appUrl: 'http://localhost:3000',
10
+ devices,
11
+ webServerCommand: 'pnpm dev',
12
+ };
13
+
14
+ describe('validation', () => {
15
+ it('throws if appUrl is missing', () => {
16
+ expect(() => buildPlaywrightBaseConfig({ ...validParams, appUrl: '' })).toThrow(
17
+ '[playwright] appUrl must be a non-empty string',
18
+ );
19
+ });
20
+
21
+ it('throws if appPort is not a number', () => {
22
+ expect(() =>
23
+ buildPlaywrightBaseConfig({ ...validParams, appPort: '3000' as unknown as number }),
24
+ ).toThrow('[playwright] appPort must be a number');
25
+ });
26
+
27
+ it('throws if devices is missing', () => {
28
+ expect(() =>
29
+ buildPlaywrightBaseConfig({
30
+ ...validParams,
31
+ devices: undefined as unknown as typeof devices,
32
+ }),
33
+ ).toThrow('[playwright] devices must be provided');
34
+ });
35
+ });
36
+
37
+ describe('config output', () => {
38
+ it('returns valid Playwright config structure', () => {
39
+ const config = buildPlaywrightBaseConfig(validParams);
40
+
41
+ expect(config).toHaveProperty('projects');
42
+ expect(config).toHaveProperty('testDir', './tests');
43
+ expect(config).toHaveProperty('testMatch', '**/*.e2e.ts');
44
+ expect(config).toHaveProperty('webServer');
45
+ expect(config).toHaveProperty('use.baseURL', 'http://localhost:3000');
46
+ });
47
+
48
+ it('configures 6 browser projects', () => {
49
+ const config = buildPlaywrightBaseConfig(validParams);
50
+ expect(config.projects).toHaveLength(6);
51
+ });
52
+
53
+ it('sets retries to 0 when not in CI', () => {
54
+ const config = buildPlaywrightBaseConfig({ ...validParams, ci: false });
55
+ expect(config.retries).toBe(0);
56
+ });
57
+
58
+ it('sets retries to 2 in CI', () => {
59
+ const config = buildPlaywrightBaseConfig({ ...validParams, ci: true });
60
+ expect(config.retries).toBe(2);
61
+ });
62
+
63
+ it('sets workers to 1 in CI', () => {
64
+ const config = buildPlaywrightBaseConfig({ ...validParams, ci: true });
65
+ expect(config.workers).toBe(1);
66
+ });
67
+
68
+ it('sets forbidOnly to true in CI', () => {
69
+ const config = buildPlaywrightBaseConfig({ ...validParams, ci: true });
70
+ expect(config.forbidOnly).toBe(true);
71
+ });
72
+ });
73
+ });
@@ -0,0 +1,63 @@
1
+ import type { PlaywrightTestConfig } from '@playwright/test';
2
+
3
+ export interface BuildPlaywrightBaseConfigParams {
4
+ appUrl: string;
5
+ appPort: number;
6
+ devices: typeof import('@playwright/test').devices;
7
+ ci?: boolean;
8
+ webServerCommand: string;
9
+ }
10
+
11
+ /**
12
+ * Build a base Playwright config object with Regardio defaults.
13
+ * Consumers should wrap with defineConfig() in their local playwright.config.ts
14
+ */
15
+ export function buildPlaywrightBaseConfig({
16
+ appUrl,
17
+ appPort,
18
+ devices,
19
+ ci = !!process.env.CI,
20
+ webServerCommand,
21
+ }: BuildPlaywrightBaseConfigParams): PlaywrightTestConfig {
22
+ if (!appUrl || typeof appUrl !== 'string') {
23
+ throw new Error('[playwright] appUrl must be a non-empty string');
24
+ }
25
+ if (!appPort || typeof appPort !== 'number') {
26
+ throw new Error('[playwright] appPort must be a number');
27
+ }
28
+ if (!devices) {
29
+ throw new Error('[playwright] devices must be provided from "@playwright/test"');
30
+ }
31
+
32
+ return {
33
+ forbidOnly: ci,
34
+ fullyParallel: true,
35
+ outputDir: './tests/test-results',
36
+ projects: [
37
+ { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
38
+ { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
39
+ { name: 'webkit', use: { ...devices['Desktop Safari'] } },
40
+ {
41
+ name: 'iPad Pro 11 landscape',
42
+ use: { ...devices['iPad Pro 11 landscape'] },
43
+ },
44
+ { name: 'iPhone 14 portrait', use: { ...devices['iPhone 14'] } },
45
+ { name: 'Pixel 7 portrait', use: { ...devices['Pixel 7'] } },
46
+ ],
47
+ reporter: [['html', { open: 'never', outputFolder: './tests/playwright-report' }]],
48
+ retries: ci ? 2 : 0,
49
+ testDir: './tests',
50
+ testMatch: '**/*.e2e.ts',
51
+ use: {
52
+ baseURL: appUrl,
53
+ trace: 'on-first-retry',
54
+ },
55
+ webServer: {
56
+ command: webServerCommand,
57
+ ignoreHTTPSErrors: true,
58
+ reuseExistingServer: !ci,
59
+ url: appUrl,
60
+ },
61
+ ...(ci ? { workers: 1 } : {}),
62
+ };
63
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Test setup file for React packages using Vitest and Testing Library.
3
+ * Import this in your vitest.config.ts setupFiles array.
4
+ *
5
+ * Usage in vitest.config.ts:
6
+ * setupFiles: ['@regardio/dev/testing/setup-react']
7
+ */
8
+ import '@testing-library/jest-dom/vitest';