Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cli/src/constructs/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export class Session {
static privateLocations: PrivateLocationApi[]
static parsers = new Map<string, Parser>()
static constructExports: ConstructExport[] = []
static ignoreDirectoriesMatch: string[] = []

static async loadFile<T = unknown> (filePath: string): Promise<T> {
const loader = this.loader
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Allow node_modules in test fixtures
!node_modules
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"mockUser": {
"id": 1,
"name": "Test User"
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "playwright-bundle-test",
"version": "1.0.0",
"dependencies": {
"@playwright/test": "^1.40.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from '@playwright/test'

export default defineConfig({
testDir: './tests',
timeout: 30000,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { test, expect } from '@playwright/test'

test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/')
expect(await page.title()).toContain('Playwright')
})
158 changes: 157 additions & 1 deletion packages/cli/src/services/__tests__/util.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import path from 'node:path'
import { describe, it, expect } from 'vitest'
import fs from 'node:fs/promises'
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { extract } from 'tar'

import {
pathToPosix,
isFileSync,
getPlaywrightVersionFromPackage,
bundlePlayWrightProject,
} from '../util'
import { Session } from '../../constructs/project'

describe('util', () => {
describe('pathToPosix()', () => {
Expand Down Expand Up @@ -46,4 +50,156 @@ describe('util', () => {
expect(version).toMatch(/^\d+\.\d+\.\d+/)
})
})

describe('bundlePlayWrightProject()', () => {
let originalBasePath: string | undefined
let extractDir: string

beforeEach(async () => {
// Save original Session state
originalBasePath = Session.basePath

// Set up Session for bundling
const fixtureDir = path.join(__dirname, 'fixtures', 'playwright-bundle-test')
Session.basePath = fixtureDir

// Create temp directory for extraction
extractDir = await fs.mkdtemp(path.join(__dirname, 'temp-extract-'))
})

afterEach(async () => {
// Restore Session state
Session.basePath = originalBasePath
Session.ignoreDirectoriesMatch = []

// Clean up extraction directory
try {
await fs.rm(extractDir, { recursive: true, force: true })
} catch {
// Ignore cleanup errors
}
})

it('should exclude directories matching ignoreDirectoriesMatch pattern', async () => {
const fixtureDir = path.join(__dirname, 'fixtures', 'playwright-bundle-test')
const playwrightConfigPath = path.join(fixtureDir, 'playwright.config.ts')

// Set ignoreDirectoriesMatch to exclude fixtures directory
Session.ignoreDirectoriesMatch = ['**/fixtures/**']

// Bundle the project
const result = await bundlePlayWrightProject(playwrightConfigPath, [])

// Extract the bundle
await extract({
file: result.outputFile,
cwd: extractDir,
})

// Check that test files are included
const testsDir = path.join(extractDir, 'tests')
const testFiles = await fs.readdir(testsDir)
expect(testFiles).toContain('example.spec.ts')

// Check that fixtures directory is NOT included
const fixturesPath = path.join(extractDir, 'fixtures')
await expect(fs.access(fixturesPath)).rejects.toThrow()
}, 30000)

it('should include all directories when ignoreDirectoriesMatch is empty', async () => {
const fixtureDir = path.join(__dirname, 'fixtures', 'playwright-bundle-test')
const playwrightConfigPath = path.join(fixtureDir, 'playwright.config.ts')

// Set empty ignoreDirectoriesMatch
Session.ignoreDirectoriesMatch = []

// Bundle the project with include pattern that matches fixtures
const result = await bundlePlayWrightProject(playwrightConfigPath, ['fixtures/**/*'])

// Extract the bundle
await extract({
file: result.outputFile,
cwd: extractDir,
})

// Check that fixtures directory IS included when explicitly in include
const fixturesPath = path.join(extractDir, 'fixtures')
const fixturesExists = await fs.access(fixturesPath).then(() => true).catch(() => false)
expect(fixturesExists).toBe(true)

if (fixturesExists) {
const fixtureFiles = await fs.readdir(fixturesPath)
expect(fixtureFiles).toContain('mock-data.json')
}
}, 30000)

it('should include explicit node_modules patterns bypassing default ignores', async () => {
const fixtureDir = path.join(__dirname, 'fixtures', 'playwright-bundle-test')
const playwrightConfigPath = path.join(fixtureDir, 'playwright.config.ts')

// Set empty ignoreDirectoriesMatch
Session.ignoreDirectoriesMatch = []

// Bundle the project with explicit node_modules pattern
const result = await bundlePlayWrightProject(playwrightConfigPath, ['node_modules/@internal/test-helpers/**'])

// Extract the bundle
await extract({
file: result.outputFile,
cwd: extractDir,
})

// Check that node_modules directory IS included when explicitly specified
const nodeModulesPath = path.join(extractDir, 'node_modules', '@internal', 'test-helpers')
const nodeModulesExists = await fs.access(nodeModulesPath).then(() => true).catch(() => false)
expect(nodeModulesExists).toBe(true)

if (nodeModulesExists) {
const helperFiles = await fs.readdir(nodeModulesPath)
expect(helperFiles).toContain('helper.js')
}
}, 30000)

it('should still respect custom ignoreDirectoriesMatch for explicit patterns', async () => {
const fixtureDir = path.join(__dirname, 'fixtures', 'playwright-bundle-test')
const playwrightConfigPath = path.join(fixtureDir, 'playwright.config.ts')

// Set custom ignoreDirectoriesMatch to exclude @internal
Session.ignoreDirectoriesMatch = ['**/@internal/**']

// Bundle the project with explicit node_modules pattern
const result = await bundlePlayWrightProject(playwrightConfigPath, ['node_modules/@internal/test-helpers/**'])

// Extract the bundle
await extract({
file: result.outputFile,
cwd: extractDir,
})

// Check that @internal is NOT included (custom ignore still applies)
const nodeModulesPath = path.join(extractDir, 'node_modules', '@internal')
await expect(fs.access(nodeModulesPath)).rejects.toThrow()
}, 30000)

it('should exclude node_modules with broad patterns despite include', async () => {
const fixtureDir = path.join(__dirname, 'fixtures', 'playwright-bundle-test')
const playwrightConfigPath = path.join(fixtureDir, 'playwright.config.ts')

// Set empty ignoreDirectoriesMatch
Session.ignoreDirectoriesMatch = []

// Bundle with a broad pattern that would match node_modules but doesn't explicitly target it
const result = await bundlePlayWrightProject(playwrightConfigPath, ['**/*.js'])

// Extract the bundle
await extract({
file: result.outputFile,
cwd: extractDir,
})

// Check that node_modules is NOT included (default ignore still applies for broad patterns)
const nodeModulesPath = path.join(extractDir, 'node_modules')
await expect(fs.access(nodeModulesPath)).rejects.toThrow()
}, 30000)
})
})
1 change: 1 addition & 0 deletions packages/cli/src/services/project-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export async function parseProject (opts: ProjectParseOpts): Promise<Project> {
Session.availableRuntimes = availableRuntimes
Session.defaultRuntimeId = defaultRuntimeId
Session.verifyRuntimeDependencies = verifyRuntimeDependencies ?? true
Session.ignoreDirectoriesMatch = ignoreDirectoriesMatch

// TODO: Do we really need all of the ** globs, or could we just put node_modules?
const ignoreDirectories = ['**/node_modules/**', '**/.git/**', ...ignoreDirectoriesMatch]
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/src/services/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ export async function loadPlaywrightProjectFiles (
dir: string, pwConfigParsed: PlaywrightConfig, include: string[], archive: Archiver,
lockFile: string,
) {
const ignoredFiles = ['**/node_modules/**', '.git/**']
const ignoredFiles = ['**/node_modules/**', '.git/**', ...Session.ignoreDirectoriesMatch]
const parser = new Parser({})
const { files, errors } = await parser.getFilesAndDependencies(pwConfigParsed)
if (errors.length) {
Expand Down Expand Up @@ -325,7 +325,15 @@ export async function loadPlaywrightProjectFiles (
prefix,
})
for (const includePattern of include) {
archive.glob(includePattern, { cwd: root }, {
// If pattern explicitly targets an ignored directory, only apply custom ignores
const explicitlyTargetsIgnored =
includePattern.startsWith('node_modules/')
|| includePattern.startsWith('.git/')

archive.glob(includePattern, {
cwd: root,
ignore: explicitlyTargetsIgnored ? Session.ignoreDirectoriesMatch : ignoredFiles,
}, {
...entryDefaults,
})
}
Expand Down
Loading