Skip to content

Commit 2cb8499

Browse files
committed
feat: tie it all together
1 parent 7b04425 commit 2cb8499

File tree

11 files changed

+611
-153
lines changed

11 files changed

+611
-153
lines changed

packages/cli/src/constructs/construct-diagnostics.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,20 @@ export class UnsupportedRuntimeFeatureDiagnostic extends ErrorDiagnostic {
137137
}
138138
}
139139

140+
export class UnsatisfiedLocalPrerequisitesDiagnostic extends ErrorDiagnostic {
141+
constructor (error: Error) {
142+
super({
143+
title: `Unsatisfied local prerequisites`,
144+
message:
145+
`Local environment does not satisfy the prerequisites for this `
146+
+ `functionality.`
147+
+ `\n\n`
148+
+ `Cause: ${error.message}`,
149+
error,
150+
})
151+
}
152+
}
153+
140154
export class ConstructDiagnostic extends Diagnostic {
141155
underlying: Diagnostic
142156

packages/cli/src/constructs/playwright-check.ts

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ import {
1111
ConflictingPropertyDiagnostic,
1212
DeprecatedPropertyDiagnostic,
1313
InvalidPropertyValueDiagnostic,
14+
UnsatisfiedLocalPrerequisitesDiagnostic,
1415
UnsupportedPropertyDiagnostic,
1516
} from './construct-diagnostics'
16-
import { Diagnostics } from './diagnostics'
17+
import { Diagnostics, ErrorDiagnostic } from './diagnostics'
1718
import { PlaywrightCheckBundle } from './playwright-check-bundle'
1819
import { Session } from './project'
1920
import { Ref } from './ref'
@@ -126,7 +127,7 @@ export interface PlaywrightCheckProps extends Omit<RuntimeCheckProps, 'retryStra
126127
*/
127128
export class PlaywrightCheck extends RuntimeCheck {
128129
installCommand?: string
129-
testCommand: string
130+
testCommand?: string
130131
playwrightConfigPath: string
131132
pwProjects: string[]
132133
pwTags: string[]
@@ -149,7 +150,7 @@ export class PlaywrightCheck extends RuntimeCheck {
149150
this.include = config.include
150151
? (Array.isArray(config.include) ? config.include : [config.include])
151152
: []
152-
this.testCommand = config.testCommand ?? 'npx playwright test'
153+
this.testCommand = config.testCommand
153154
this.groupName = config.groupName
154155
this.playwrightConfigPath = this.resolveContentFilePath(config.playwrightConfigPath)
155156
Session.registerConstruct(this)
@@ -240,6 +241,7 @@ export class PlaywrightCheck extends RuntimeCheck {
240241

241242
async validate (diagnostics: Diagnostics): Promise<void> {
242243
await super.validate(diagnostics)
244+
await this.#validateWorkspace(diagnostics)
243245
await this.validateRetryStrategy(diagnostics)
244246

245247
try {
@@ -254,6 +256,29 @@ export class PlaywrightCheck extends RuntimeCheck {
254256
this.#validateGroupReferences(diagnostics)
255257
}
256258

259+
// eslint-disable-next-line require-await
260+
async #validateWorkspace (diagnostics: Diagnostics): Promise<void> {
261+
const workspace = Session.workspace
262+
if (workspace.isOk()) {
263+
const lockfile = workspace.ok().lockfile
264+
if (lockfile.isErr()) {
265+
diagnostics.add(new UnsatisfiedLocalPrerequisitesDiagnostic(new Error(
266+
`A lockfile is required for Playwright checks, but none could be `
267+
+ `detected.`
268+
+ '\n\n'
269+
+ `Cause: ${lockfile.err().message}`,
270+
)))
271+
}
272+
} else if (workspace.isErr()) {
273+
diagnostics.add(new UnsatisfiedLocalPrerequisitesDiagnostic(new Error(
274+
`A workspace is required for Playwright checks, but none could be `
275+
+ `detected.`
276+
+ '\n\n'
277+
+ `Cause: ${workspace.err().message}`,
278+
)))
279+
}
280+
}
281+
257282
#validateGroupReferences (diagnostics: Diagnostics): void {
258283
if (this.groupName) {
259284
diagnostics.add(new DeprecatedPropertyDiagnostic(
@@ -295,6 +320,12 @@ export class PlaywrightCheck extends RuntimeCheck {
295320
return `${testCommand} --config ${quotedPath}${projectArg}${tagArg}`
296321
}
297322

323+
static contextifyCommand (command: string): string {
324+
return Session.basePath === Session.contextPath
325+
? command
326+
: `env --chdir "${Session.relativePosixPath(Session.contextPath!)}" -- ${command}`
327+
}
328+
298329
static async bundleProject (playwrightConfigPath: string, include: string[]) {
299330
let dir = ''
300331
try {
@@ -333,12 +364,12 @@ export class PlaywrightCheck extends RuntimeCheck {
333364
relativePlaywrightConfigPath,
334365
} = await PlaywrightCheck.bundleProject(this.playwrightConfigPath, this.include ?? [])
335366

336-
const testCommand = PlaywrightCheck.buildTestCommand(
337-
this.testCommand,
367+
const testCommand = PlaywrightCheck.contextifyCommand(PlaywrightCheck.buildTestCommand(
368+
this.testCommand ?? this.#defaultTestCommand(),
338369
relativePlaywrightConfigPath,
339370
this.pwProjects,
340371
this.pwTags,
341-
)
372+
))
342373

343374
return new PlaywrightCheckBundle(this, {
344375
groupId,
@@ -351,6 +382,10 @@ export class PlaywrightCheck extends RuntimeCheck {
351382
})
352383
}
353384

385+
#defaultTestCommand (): string {
386+
return Session.packageManager.execCommand(['playwright', 'test']).unsafeDisplayCommand
387+
}
388+
354389
synthesize () {
355390
return {
356391
...super.synthesize(),

packages/cli/src/constructs/project.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import { ConstructDiagnostics, InvalidPropertyValueDiagnostic } from './construc
2525
import { ProjectBundle, ProjectDataBundle } from './project-bundle'
2626
import { pathToPosix } from '../services/util'
2727
import { Workspace } from '../services/check-parser/package-files/workspace'
28+
import { npmPackageManager, PackageManager } from '../services/check-parser/package-files/package-manager'
29+
import { Err, Result } from '../services/check-parser/package-files/result'
2830

2931
export interface ProjectProps {
3032
/**
@@ -230,6 +232,7 @@ export class Session {
230232

231233
static project?: Project
232234
static basePath?: string
235+
static contextPath?: string
233236
static checkDefaults?: CheckConfigDefaults
234237
static checkFilter?: CheckFilter
235238
static browserCheckDefaults?: CheckConfigDefaults
@@ -245,7 +248,8 @@ export class Session {
245248
static parsers = new Map<string, Parser>()
246249
static constructExports: ConstructExport[] = []
247250
static ignoreDirectoriesMatch: string[] = []
248-
static workspace?: Workspace
251+
static packageManager: PackageManager = npmPackageManager
252+
static workspace: Result<Workspace, Error> = Err(new Error(`Workspace support not initialized`))
249253

250254
static async loadFile<T = unknown> (filePath: string): Promise<T> {
251255
const loader = this.loader
@@ -340,7 +344,7 @@ export class Session {
340344
const parser = new Parser({
341345
supportedNpmModules: Object.keys(runtime.dependencies),
342346
checkUnsupportedModules: Session.verifyRuntimeDependencies,
343-
workspace: Session.workspace,
347+
workspace: Session.workspace.ok(),
344348
})
345349

346350
Session.parsers.set(runtime.name, parser)
@@ -352,6 +356,10 @@ export class Session {
352356
return pathToPosix(path.relative(Session.basePath!, filePath))
353357
}
354358

359+
static contextRelativePosixPath (filePath: string): string {
360+
return pathToPosix(path.relative(Session.contextPath!, filePath))
361+
}
362+
355363
static sharedFileRefs = new Map<string, SharedFileRef>()
356364
static sharedFiles: SharedFile[] = []
357365

packages/cli/src/services/check-parser/package-files/extension.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ export type CoreExtension = '.js' | '.mjs' | '.cjs' | '.json'
44

55
export const CoreExtensions: CoreExtension[] = ['.js', '.mjs', '.cjs', '.json']
66

7+
export function isCoreExtension (value: string): value is CoreExtension {
8+
return CoreExtensions.includes(value as CoreExtension)
9+
}
10+
711
type CoreExtensionMapping = {
812
[key in CoreExtension]: string[]
913
}
@@ -26,9 +30,13 @@ export const tsCoreExtensionLookupOrder: CoreExtensionMapping = {
2630
'.json': ['.json'],
2731
}
2832

29-
export type TSExtension = '.ts' | '.mts' | '.tsx'
33+
export type TSExtension = '.ts' | '.mts' | '.cts' | '.tsx'
3034

31-
export const TSExtensions: TSExtension[] = ['.ts', '.mts', '.tsx']
35+
export const TSExtensions: TSExtension[] = ['.ts', '.mts', '.cts', '.tsx']
36+
37+
export function isTSExtension (value: string): value is TSExtension {
38+
return TSExtensions.includes(value as TSExtension)
39+
}
3240

3341
type TSExtensionMapping = {
3442
[key in TSExtension]: string[]
@@ -37,6 +45,7 @@ type TSExtensionMapping = {
3745
export const tsExtensionLookupOrder: TSExtensionMapping = {
3846
'.ts': ['.ts'],
3947
'.mts': ['.mts'],
48+
'.cts': ['.cts'],
4049
'.tsx': ['.tsx'],
4150
}
4251

0 commit comments

Comments
 (0)