diff --git a/.github/workflows/positron-merge-to-main.yml b/.github/workflows/positron-merge-to-main.yml index f147f07a3ab..c5a28c69d6d 100644 --- a/.github/workflows/positron-merge-to-main.yml +++ b/.github/workflows/positron-merge-to-main.yml @@ -27,6 +27,56 @@ permissions: contents: read jobs: + e2e-playwright: + timeout-minutes: 30 + runs-on: ubuntu-latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + POSITRON_BUILD_NUMBER: 0 # CI skips building releases + _R_CHECK_FUTURE_FILE_TIMESTAMPS_: false # this check can be flaky in the R pkg tests + _R_CHECK_CRAN_INCOMING_: false + _R_CHECK_SYSTEM_CLOCK_: false + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + + - name: Cache node_modules, build, extensions, and remote + uses: ./.github/actions/cache-multi-paths + + - name: Setup Build and Compile + uses: ./.github/actions/setup-build-env + + - name: Setup E2E Test Environment + uses: ./.github/actions/setup-test-env + with: + aws-role-to-assume: ${{ secrets.QA_AWS_RO_ROLE }} + aws-region: ${{ secrets.QA_AWS_REGION }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Compile E2E Tests + run: yarn --cwd test/automation compile && yarn --cwd test/smoke compile + + # - name: Setup tmate session + # uses: mxschmitt/action-tmate@v3 + + - name: Run Tests (Electron) + env: + POSITRON_PY_VER_SEL: 3.10.12 + POSITRON_R_VER_SEL: 4.4.0 + TEST: "marie123!" + id: e2e-playwright-tests + run: DISPLAY=:10 npx playwright test --project e2e-electron --grep @pr --workers 2 + + - name: Upload Playwright Report + uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 7 linux: name: Tests on Linux runs-on: ubuntu-latest-8x @@ -63,6 +113,9 @@ jobs: aws-region: ${{ secrets.QA_AWS_REGION }} github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Compile E2E Tests + run: yarn --cwd test/automation compile && yarn --cwd test/smoke compile + - name: Compile Integration Tests run: yarn --cwd test/integration/browser compile diff --git a/.gitignore b/.gitignore index 232848ee7d8..f3817680eff 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,6 @@ product.overrides.json # --- Start Positron --- .Rproj.user # --- End Positron --- - +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/package.json b/package.json index 05b4e8d314f..0df73f039e4 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "yazl": "^2.4.3" }, "devDependencies": { - "@playwright/test": "^1.46.1", + "@playwright/test": "^1.48.0", "@swc/core": "1.3.62", "@types/cookie": "^0.3.3", "@types/debug": "^4.1.5", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000000..3a729c4af3f --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { defineConfig, PlaywrightTestOptions } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// import path from 'path'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +export type CustomTestOptions = PlaywrightTestOptions & { + web: boolean; + artifactDir: string; + headless?: boolean; +}; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + globalSetup: require.resolve('./test/smoke/src/e2e/_global.setup.ts'), + testDir: './test/smoke/src/e2e', + testMatch: '*.test.ts', + fullyParallel: false, // Run individual tests in parallel + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 0 : 0, + workers: 3, // Number of parallel workers (tests will run in parallel) + timeout: 2 * 60 * 1000, // test timeout is 2 minutes + reportSlowTests: { + max: 10, + threshold: 60 * 1000, // 1 minute + }, + reporter: process.env.CI + ? [ + ['github'], + ['junit', { outputFile: 'test-results/results.xml' }], + ['html'] + ] + : [ + ['list'], + ['html'] + ], + + + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + headless: false, + trace: 'off', // we are manually handling tracing in _test.setup.ts + }, + + projects: [ + { + name: 'e2e-electron', + use: { + web: false, + artifactDir: 'e2e-electron' + }, + }, + { + name: 'e2e-chromium', + use: { + web: true, + artifactDir: 'e2e-chromium', + headless: true, + }, + grep: /@web/ + }, + ], +}); diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 2a0084e1c1d..d7a0d3a09f2 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -104,8 +104,8 @@ export class Application { await this._code?.startTracing(name); } - async stopTracing(name: string, persist: boolean): Promise { - await this._code?.stopTracing(name, persist); + async stopTracing(name: string, persist: boolean, customPath?: string): Promise { + await this._code?.stopTracing(name, persist, customPath); } private async startApplication(extraArgs: string[] = []): Promise { diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index a925cdd65bc..584f93b99a9 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -121,8 +121,8 @@ export class Code { return await this.driver.startTracing(name); } - async stopTracing(name: string, persist: boolean): Promise { - return await this.driver.stopTracing(name, persist); + async stopTracing(name: string, persist: boolean, customPath?: string): Promise { + return await this.driver.stopTracing(name, persist, customPath); } async dispatchKeybinding(keybinding: string): Promise { diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index 5adaa33d7d8..0b2e124021f 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -105,7 +105,7 @@ async function launchBrowser(options: LaunchOptions, endpoint: string) { if (tracing) { try { - await measureAndLog(() => context.tracing.start({ screenshots: true, /* remaining options are off for perf reasons */ }), 'context.tracing.start()', logger); + await measureAndLog(() => context.tracing.start({ screenshots: true, snapshots: true, /* remaining options are off for perf reasons */ }), 'context.tracing.start()', logger); } catch (error) { logger.log(`Playwright (Browser): Failed to start playwright tracing (${error})`); // do not fail the build when this fails } diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 55783b1355b..0323b881277 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -35,8 +35,8 @@ export class PlaywrightDriver { constructor( private readonly application: playwright.Browser | playwright.ElectronApplication, - private readonly context: playwright.BrowserContext, // --- Start Positron --- + readonly context: playwright.BrowserContext, readonly page: playwright.Page, // --- End Positron --- private readonly serverProcess: ChildProcess | undefined, @@ -49,7 +49,6 @@ export class PlaywrightDriver { if (!this.options.tracing) { return; // tracing disabled } - try { await measureAndLog(() => this.context.tracing.startChunk({ title: name }), `startTracing for ${name}`, this.options.logger); } catch (error) { @@ -57,7 +56,7 @@ export class PlaywrightDriver { } } - async stopTracing(name: string, persist: boolean): Promise { + async stopTracing(name: string, persist: boolean = true, customPath?: string): Promise { if (!this.options.tracing) { return; // tracing disabled } @@ -67,21 +66,10 @@ export class PlaywrightDriver { if (persist) { // --- Start Positron --- // Positron: Windows has issues with long paths, shortened the name - persistPath = join(this.options.logsPath, `trace-${PlaywrightDriver.traceCounter++}-${name.replace(/\s+/g, '-')}.zip`); + persistPath = customPath || join(this.options.logsPath, `trace-${PlaywrightDriver.traceCounter++}-${name.replace(/\s+/g, '-')}.zip`); // --- End Positron --- } - await measureAndLog(() => this.context.tracing.stopChunk({ path: persistPath }), `stopTracing for ${name}`, this.options.logger); - - // To ensure we have a screenshot at the end where - // it failed, also trigger one explicitly. Tracing - // does not guarantee to give us a screenshot unless - // some driver action ran before. - if (persist) { - // --- Start Positron --- - await this.takeScreenshot(`${name}`); - // --- End Positron --- - } } catch (error) { // Ignore } diff --git a/test/automation/src/playwrightElectron.ts b/test/automation/src/playwrightElectron.ts index 660320be235..b0761e4d0e4 100644 --- a/test/automation/src/playwrightElectron.ts +++ b/test/automation/src/playwrightElectron.ts @@ -45,7 +45,7 @@ async function launchElectron(configuration: IElectronConfiguration, options: La if (tracing) { try { - await measureAndLog(() => context.tracing.start({ screenshots: true, /* remaining options are off for perf reasons */ }), 'context.tracing.start()', logger); + await measureAndLog(() => context.tracing.start({ screenshots: true, snapshots: true /* remaining options are off for perf reasons */ }), 'context.tracing.start()', logger); } catch (error) { logger.log(`Playwright (Electron): Failed to start playwright tracing (${error})`); // do not fail the build when this fails } diff --git a/test/smoke/README.md b/test/smoke/README.md index e74a7cec61c..46a8ec83179 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -167,7 +167,7 @@ Make sure that you have followed the [Machine Setup](https://connect.posit.it/po ### Test Dependencies -Several tests use [QA Content Examples](https://github.com/posit-dev/qa-example-content). You will need to install the dependencies for those projects. A few current tests also use additional packages. You can look in the [positron-full-test.yml](https://github.com/posit-dev/positron/blob/39a01b71064e2ef3ef5822c95691a034b7e0194f/.github/workflows/positron-full-test.yml) Github action for the full list. +Several tests use [QA Content Examples](https://github.com/posit-dev/qa-example-content). You will need to install the dependencies for those projects. A few current tests also use additional packages. You can look in the [positron-full-test.yml](https://github.com/posit-dev/positron/blob/39a01b71064e2ef3ef5822c95691a034b7e0194f/.github/workflows/positron-full-test.yml) Github action for the full list. ## Running Tests @@ -235,9 +235,9 @@ yarn smoketest-pr --build /Applications/Positron.app --parallel --jobs 3 ## Test Project -Before any of the tests start executing the test framework clones down the [QA Content Examples](https://github.com/posit-dev/qa-example-content) repo. This repo contains R and Python files that are run by the automated tests and also includes data files (such as Excel, SQLite, & parquet) that support the test scripts. If you make additions to QA Content Examples for a test, please be sure that the data files are free to use in a public repository. +Before any of the tests start executing the test framework clones down the [QA Content Examples](https://github.com/posit-dev/qa-example-content) repo. This repo contains R and Python files that are run by the automated tests and also includes data files (such as Excel, SQLite, & parquet) that support the test scripts. If you make additions to QA Content Examples for a test, please be sure that the data files are free to use in a public repository. -For Python, add any package requirements to the `requirements.txt` file in the root of the [QA Content Examples](https://github.com/posit-dev/qa-example-content) repo. We generally do NOT pin them to a specific version, as test can be run against different versions of python and conflicts could arise. If this becomes a problem, we can revisit this mechanism. +For Python, add any package requirements to the `requirements.txt` file in the root of the [QA Content Examples](https://github.com/posit-dev/qa-example-content) repo. We generally do NOT pin them to a specific version, as test can be run against different versions of python and conflicts could arise. If this becomes a problem, we can revisit this mechanism. For R, add any package requirements to the "imports" section of the `DESCRIPTION` file in the root of the [QA Content Examples](https://github.com/posit-dev/qa-example-content) repo. @@ -262,26 +262,26 @@ await app.workbench.quickaccess.runCommand('workbench.action.toggleDevTools');` ### Playwright Traces -Note that in launch.json for `Launch Smoke Test` we are passing the `--tracing` argument for you. This will result in Playwright traces being generated locally for you when tests fail at `.build/logs/smoke-tests-electron/{testCase}`. Note that for command line runs you will need to pass this arg yourself to get the trace file(s). +Note that in launch.json for `Launch Smoke Test` we are passing the `--tracing` argument for you. This will result in Playwright traces being generated locally for you when tests fail at `.build/logs/smoke-tests-electron/{testCase}`. Note that for command line runs you will need to pass this arg yourself to get the trace file(s). ## Running Tests in Github Actions -New tests are not complete until they run successfully across operating systems (Mac, Windows, & Ubuntu) and in [Github Actions](https://github.com/posit-dev/positron/actions/workflows/positron-full-test.yml). In Github Actions we use an Ubuntu instance to run the tests, so if you are developing your tests using a Mac or on Windows, this is an opportunity to test a different operating system. Also, you can easily run your new tests against a branch to verify them before merge. Simply pick the branch after you click on "Run Workflow". Note that you can also temporarily modify the workflow itself to get your new tests executed more quickly. To do this, skip the runs of the unit and integration tests. +New tests are not complete until they run successfully across operating systems (Mac, Windows, & Ubuntu) and in [Github Actions](https://github.com/posit-dev/positron/actions/workflows/positron-full-test.yml). In Github Actions we use an Ubuntu instance to run the tests, so if you are developing your tests using a Mac or on Windows, this is an opportunity to test a different operating system. Also, you can easily run your new tests against a branch to verify them before merge. Simply pick the branch after you click on "Run Workflow". Note that you can also temporarily modify the workflow itself to get your new tests executed more quickly. To do this, skip the runs of the unit and integration tests. ### Github Actions Test Artifacts -When a run is complete, you can debug any test failures that occurred using the uploaded run artifacts. The artifacts are available as a ZIP file from inside the workflow run. Each artifact zip contains: a folder for each test file and an overall run log. Inside the folder corresponding to each test file, you will find zip files that are Playwright traces. Note that the trace files are only present for failed cases. +When a run is complete, you can debug any test failures that occurred using the uploaded run artifacts. The artifacts are available as a ZIP file from inside the workflow run. Each artifact zip contains: a folder for each test file and an overall run log. Inside the folder corresponding to each test file, you will find zip files that are Playwright traces. Note that the trace files are only present for failed cases. -Playwright traces can be drag and dropped to the [Trace Viewer](https://trace.playwright.dev/). The trace will usually give you a good visualization of the failed test, but they can be sparse on details. More details are available from the run log (smoke-test-runner.log). It has a start and end marker for each test case. +Playwright traces can be drag and dropped to the [Trace Viewer](https://trace.playwright.dev/). The trace will usually give you a good visualization of the failed test, but they can be sparse on details. More details are available from the run log (e2e-test-runner.log). It has a start and end marker for each test case. ## Notes About Updating Specific Tests ### Plot Tests That Use Resemblejs -In order to get the "golden screenshots" used for plot comparison is CI, you will need to temporarily uncomment the line of code marked with `capture master image in CI` or add a similar line of code for a new case. We must use CI taken snapshots because if the "golden screenshots" are taken locally, they will differ too much from the CI images to be useable with a proper threshold. You can't compare the current runtime plot against a snapshot until you have established a baseline screenshot from CI that is saved to `test/smoke/plots`. +In order to get the "golden screenshots" used for plot comparison is CI, you will need to temporarily uncomment the line of code marked with `capture master image in CI` or add a similar line of code for a new case. We must use CI taken snapshots because if the "golden screenshots" are taken locally, they will differ too much from the CI images to be useable with a proper threshold. You can't compare the current runtime plot against a snapshot until you have established a baseline screenshot from CI that is saved to `test/smoke/plots`. ## Tests run on PRs -If you think your test should be run when PRs are created, add the string `#pr` to its name. The existing #pr cases were selected to give good overall coverage while keeping the overall execution time down to ten minutes or less. If your new test functionality covers a part of the application that no other tests cover, it is probably a good idea to include it in the #pr set. +If you think your test should be run when PRs are created, add the string `#pr` to its name. The existing #pr cases were selected to give good overall coverage while keeping the overall execution time down to ten minutes or less. If your new test functionality covers a part of the application that no other tests cover, it is probably a good idea to include it in the #pr set. diff --git a/test/smoke/package.json b/test/smoke/package.json index 9ed74b9ccfd..9d325fbbaba 100644 --- a/test/smoke/package.json +++ b/test/smoke/package.json @@ -26,6 +26,7 @@ "@types/node-fetch": "^2.5.10", "@types/resemblejs": "4.1.3", "@types/rimraf": "3.0.2", + "archiver": "^7.0.1", "npm-run-all": "^4.1.5", "watch": "^1.0.2" } diff --git a/test/smoke/src/e2e/_global.setup.ts b/test/smoke/src/e2e/_global.setup.ts new file mode 100644 index 00000000000..613cf9434e7 --- /dev/null +++ b/test/smoke/src/e2e/_global.setup.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { join } from 'path'; +import * as os from 'os'; +import * as fs from 'fs'; +import { cloneTestRepo, prepareTestEnv } from '../test-runner'; + +const ROOT_PATH = join(__dirname, '..', '..', '..', '..'); +const LOGS_ROOT_PATH = join(ROOT_PATH, '.build', 'logs'); +const TEST_DATA_PATH = join(os.tmpdir(), 'vscsmoke'); +const WORKSPACE_PATH = join(TEST_DATA_PATH, 'qa-example-content'); + +async function globalSetup() { + fs.rmSync(LOGS_ROOT_PATH, { recursive: true, force: true }); + prepareTestEnv(ROOT_PATH); + cloneTestRepo(WORKSPACE_PATH); +} + +export default globalSetup; diff --git a/test/smoke/src/e2e/_test.setup.ts b/test/smoke/src/e2e/_test.setup.ts new file mode 100644 index 00000000000..1ed4cec75c4 --- /dev/null +++ b/test/smoke/src/e2e/_test.setup.ts @@ -0,0 +1,276 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +// Playwright and testing imports +import * as playwright from '@playwright/test'; +const { test: base, expect: playwrightExpect } = playwright; + +// Node.js built-in modules +import { join } from 'path'; +import * as os from 'os'; +import * as fs from 'fs'; +import path = require('path'); +// eslint-disable-next-line local/code-import-patterns +import { rename, rm, access, mkdir } from 'fs/promises'; +import { constants } from 'fs'; + +// Third-party packages +import { randomUUID } from 'crypto'; +import archiver from 'archiver'; + +// Local imports +import { createLogger } from '../test-runner/logger'; +import { Application, Logger, PositronPythonFixtures, PositronRFixtures } from '../../../automation'; +import { createApp } from '../utils'; +import { CustomTestOptions } from '../../../../playwright.config'; + +const TEMP_DIR = `temp-${randomUUID()}`; +const ROOT_PATH = join(__dirname, '..', '..', '..', '..'); +const LOGS_ROOT_PATH = join(ROOT_PATH, '.build', 'logs'); + +let SPEC_NAME = ''; +let logsCounter = 1; + +export const test = base.extend<{ + restartApp: Application; + tracing: any; + page: playwright.Page; + context: playwright.BrowserContext; + attachScreenshotsToReport: any; + attachLogsToReport: any; + interpreter: { set: (interpreterName: 'Python' | 'R') => Promise }; + r: void; + python: void; + autoTestFixture: any; + +}, { + suiteId: string; + artifactDir: string; + options: any; + app: Application; + logsPath: string; + logger: Logger; +}>({ + + suiteId: ['', { scope: 'worker', option: true }], + + logsPath: [async ({ }, use, workerInfo) => { + const project = workerInfo.project.use as CustomTestOptions; + const logsPath = join(LOGS_ROOT_PATH, project.artifactDir, TEMP_DIR); + await use(logsPath); + }, { scope: 'worker', auto: true }], + + logger: [async ({ logsPath }, use) => { + const logger = createLogger(logsPath); + await use(logger); + }, { auto: true, scope: 'worker' }], + + options: [async ({ logsPath, logger }, use, workerInfo) => { + const project = workerInfo.project.use as CustomTestOptions; + const TEST_DATA_PATH = join(os.tmpdir(), 'vscsmoke'); + const EXTENSIONS_PATH = join(TEST_DATA_PATH, 'extensions-dir'); + const WORKSPACE_PATH = join(TEST_DATA_PATH, 'qa-example-content'); + const SPEC_CRASHES_PATH = join(ROOT_PATH, '.build', 'crashes', project.artifactDir, TEMP_DIR); + + const options = { + codePath: process.env.BUILD, + workspacePath: WORKSPACE_PATH, + userDataDir: join(TEST_DATA_PATH, 'd'), + extensionsPath: EXTENSIONS_PATH, + logger, + logsPath, + crashesPath: SPEC_CRASHES_PATH, + verbose: process.env.VERBOSE, + remote: process.env.REMOTE, + web: project.web, + headless: project.headless, + tracing: true, + }; + + await use(options); + }, { scope: 'worker', auto: true }], + + restartApp: [async ({ app }, use) => { + await app.restart(); + await use(app); + }, { scope: 'test', timeout: 60000 }], + + app: [async ({ options, logsPath }, use) => { + const app = createApp(options); + await app.start(); + await use(app); + await app.stop(); + + // rename the temp logs dir to the spec name + const specLogsPath = logsPath.split('/').slice(0, -1).join('/') + '/' + SPEC_NAME; + await moveAndOverwrite(logsPath, specLogsPath); + }, { scope: 'worker', auto: true, timeout: 60000 }], + + interpreter: [async ({ app, page }, use) => { + const setInterpreter = async (interpreterName: 'Python' | 'R') => { + const currentInterpreter = await page.locator('.top-action-bar-interpreters-manager').textContent() || ''; + + if (!currentInterpreter.includes(interpreterName)) { + if (interpreterName === 'Python') { + await PositronPythonFixtures.SetupFixtures(app); + } else if (interpreterName === 'R') { + await PositronRFixtures.SetupFixtures(app); + } + } + }; + + await use({ set: setInterpreter }); + }, { scope: 'test', }], + + r: [ + async ({ interpreter }, use) => { + await interpreter.set('R'); + await use(); + }, + { scope: 'test' } + ], + + python: [ + async ({ interpreter }, use) => { + await interpreter.set('Python'); + await use(); + }, + { scope: 'test' }], + + attachScreenshotsToReport: [async ({ app }, use, testInfo) => { + let screenShotCounter = 1; + const page = app.code.driver.page; + const screenshots: string[] = []; + + app.code.driver.takeScreenshot = async function (name: string) { + const screenshotPath = testInfo.outputPath(`${screenShotCounter++}-${name}.png`); + page.screenshot({ path: screenshotPath }); + screenshots.push(screenshotPath); + }; + + await use(); + + // if test failed, attach screenshot + if (testInfo.status !== testInfo.expectedStatus) { + const screenshot = await page.screenshot(); + await testInfo.attach('on-test-end', { body: screenshot, contentType: 'image/png' }); + } + + for (const screenshotPath of screenshots) { + testInfo.attachments.push({ name: path.basename(screenshotPath), path: screenshotPath, contentType: 'image/png' }); + } + + }, { auto: true }], + + attachLogsToReport: [async ({ suiteId, logsPath }, use, testInfo) => { + await use(); + + if (!suiteId) { return; } + + const zipPath = path.join(logsPath, 'logs.zip'); + const output = fs.createWriteStream(zipPath); + const archive = archiver('zip', { zlib: { level: 9 } }); + + archive.on('error', (err) => { + throw err; + }); + + archive.pipe(output); + + // add all log files to the archive + archive.glob('**/*', { cwd: logsPath, ignore: ['logs.zip'] }); + await archive.finalize(); + + // attach the zipped file to the report + await testInfo.attach(`logs-${suiteId}-${logsCounter++}.zip`, { + path: zipPath, + contentType: 'application/zip', + }); + + // Clear the TEMP_LOGS_PATH directory + // const files = await fs.promises.readdir(TEMP_LOGS_PATH); + // for (const file of files) { + // const filePath = path.join(TEMP_LOGS_PATH, file); + // await fs.promises.rm(filePath, { recursive: true, force: true }); + // } + }, { auto: true }], + + tracing: [async ({ app }, use, testInfo) => { + // start tracing + const title = (testInfo.title || 'unknown').replace(/\s+/g, '-'); + await app.startTracing(title); + + await use(app); + + // stop tracing + const tracePath = testInfo.outputPath(`${title}_trace.zip`); + await app.stopTracing(title, true, tracePath); + testInfo.attachments.push({ name: 'trace', path: tracePath, contentType: 'application/zip' }); + + }, { auto: true, scope: 'test' }], + + page: async ({ app }, use) => { + await use(app.code.driver.page); + }, + + context: async ({ app }, use) => { + await use(app.code.driver.context); + }, + + autoTestFixture: [async ({ logger, suiteId }, use, testInfo) => { + // if (!suiteId) { throw new Error('suiteId is required'); } + + logger.log(''); + logger.log(`>>> Test start: '${testInfo.title ?? 'unknown'}' <<<`); + logger.log(''); + + await use(); + + const failed = testInfo.status !== testInfo.expectedStatus; + const testTitle = testInfo.title; + const endLog = failed ? `>>> !!! FAILURE !!! Test end: '${testTitle}' !!! FAILURE !!! <<<` : `>>> Test end: '${testTitle}' <<<`; + + logger.log(''); + logger.log(endLog); + logger.log(''); + }, { scope: 'test', auto: true }], +}); + +// Runs once per worker. If a worker handles multiple specs, these hooks only run for the first spec. +// However, we are using `suiteId` to ensure each suite gets a new worker (and a fresh app +// instance). This also ensures these before/afterAll hooks will run for EACH spec +test.beforeAll(async ({ logger }, testInfo) => { + // since the worker doesn't know or have access to the spec name when it starts, + // we store the spec name in a global variable. this ensures logs are written + // to the correct folder even when the app is scoped to "worker". + // by storing the spec name globally, we can rename the logs folder after the suite finishes. + // note: workers are intentionally restarted per spec to scope logs by spec + // and provide a fresh app instance for each spec. + SPEC_NAME = testInfo.titlePath[0]; + logger.log(''); + logger.log(`>>> Suite start: '${testInfo.titlePath[0] ?? 'unknown'}' <<<`); + logger.log(''); +}); + +test.afterAll(async function ({ logger }, testInfo) { + logger.log(''); + logger.log(`>>> Suite end: '${testInfo.titlePath[0] ?? 'unknown'}' <<<`); + logger.log(''); +}); + +export { playwrightExpect as expect }; + +async function moveAndOverwrite(sourcePath: string, destinationPath: string) { + try { + // check if the destination exists and delete it if so + await access(destinationPath, constants.F_OK); + await rm(destinationPath, { recursive: true, force: true }); + } catch { + // if destination doesn't exist, continue without logging + } + + await mkdir(path.dirname(destinationPath), { recursive: true }); + await rename(sourcePath, destinationPath); +} diff --git a/test/smoke/src/e2e/console-ouput-log.test.ts b/test/smoke/src/e2e/console-ouput-log.test.ts new file mode 100644 index 00000000000..ec8cf7ec38e --- /dev/null +++ b/test/smoke/src/e2e/console-ouput-log.test.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { test, expect } from './_test.setup'; + +test.use({ + suiteId: 'console-output-log' +}); + +test.describe('Console Output Log', { tag: ['@web'] }, () => { + test.beforeEach(async function ({ app }) { + await app.workbench.positronLayouts.enterLayout('stacked'); + }); + + test('Python - Verify Console Output Log Contents [C667518]', async function ({ app, interpreter }) { + await interpreter.set('Python'); + + const activeConsole = app.workbench.positronConsole.activeConsole; + await activeConsole.click(); + + await app.workbench.positronConsole.typeToConsole('a = b'); + await app.workbench.positronConsole.sendEnterKey(); + + // retry in case the console output log is slow to appear + await expect(async () => { + await app.workbench.positronOutput.openOutputPane(process.env.POSITRON_PY_VER_SEL!); + await app.workbench.positronLayouts.enterLayout('fullSizedPanel'); + await app.workbench.positronOutput.waitForOutContaining("name 'b' is not defined"); + }).toPass({ timeout: 60000 }); + }); + + test('R - Verify Console Output Log Contents [C667519]', async function ({ app, interpreter }) { + await interpreter.set('R'); + + const activeConsole = app.workbench.positronConsole.activeConsole; + await activeConsole.click(); + + await app.workbench.positronConsole.typeToConsole('a = b'); + await app.workbench.positronConsole.sendEnterKey(); + + // retry in case the console output log is slow to appear + await expect(async () => { + await app.workbench.positronOutput.openOutputPane(process.env.POSITRON_R_VER_SEL!); + await app.workbench.positronLayouts.enterLayout('fullSizedPanel'); + await app.workbench.positronOutput.waitForOutContaining("object 'b' not found"); + }).toPass({ timeout: 60000 }); + }); +}); diff --git a/test/smoke/src/e2e/examples/annotations.test.ts b/test/smoke/src/e2e/examples/annotations.test.ts new file mode 100644 index 00000000000..7eb4df4652a --- /dev/null +++ b/test/smoke/src/e2e/examples/annotations.test.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { test } from '../_test.setup'; + +// Tags: +// - Run all tests WITH the @pr tag - `npx playwright test --grep @pr` +// - Run all tests WITHOUT the @slow tag - `npx playwright test --grep-invert @slow` +// - Run all tests with the @web OR @pr tag - npx playwright test --grep "@web|@pr" +// - Run all tests with the @web AND @pr tag - npx playwright test --grep "(?=.*@web)(?=.*@pr)" + +// Annotations: +// To annotate your tests with more than a tag, you can add a type and description when declaring the test. +// These annotations are available in the reporter API, and Playwright’s HTML reporter shows all annotations, +// except those starting with an underscore. + + +test.describe('annotations and tags', () => { + test('should have tags', { + tag: ['@slow', '@fast'], + }, async () => { + // This test is tagged with multiple tags. + }); + + test('should have annotations', { + annotation: [ + { type: 'category', description: 'report' }, + { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/23180' }, + { type: 'performance', description: 'very slow test!' }, + { type: 'test case', description: 'https://posit.testrail.io/index.php?/cases/view/534454' } + ], + tag: ['@pr'], + }, async () => { + // This test is annotated with multiple annotations. + }); +}); + +// Conditional skip a test based on the browser name +test.describe('chromium only', () => { + test.skip(({ browserName }) => browserName !== 'chromium', 'Chromium only!'); + + test.beforeAll(async () => { + // This hook is only run in Chromium. + }); + + test('test 1', async () => { + // This test is only run in Chromium. + }); + + test('test 2', async () => { + // This test is only run in Chromium. + }); +}); diff --git a/test/smoke/src/e2e/examples/browser.test.ts b/test/smoke/src/e2e/examples/browser.test.ts new file mode 100644 index 00000000000..fb375342951 --- /dev/null +++ b/test/smoke/src/e2e/examples/browser.test.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle(/Playwright/); +}); + +test('get started link', async ({ page }) => { + await page.goto('https://playwright.dev/'); + + // Click the get started link. + await page.getByRole('link', { name: 'Get started' }).click(); + + // Expects page to have a heading with the name of Installation. + await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible(); +}); diff --git a/test/smoke/src/e2e/examples/interpreter.test.ts b/test/smoke/src/e2e/examples/interpreter.test.ts new file mode 100644 index 00000000000..d4e5b1dfd76 --- /dev/null +++ b/test/smoke/src/e2e/examples/interpreter.test.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { test } from '../_test.setup'; + +// By default, no interpreter is set. The recommendation is to set the interpreter for EACH test. +// If the intended interpreter is already set, the interpreter will not restart/switch. +// If the intended interpreter is not set, the interpreter will start/switch. + +test.describe('Interpreter Test', () => { + test('1st test - r interpreter should start', { tag: ['@web'] }, async ({ interpreter }) => { + await interpreter.set('R'); + }); + + test('2nd test - r interpreter should NOT start', async ({ interpreter }) => { + await interpreter.set('R'); + }); + + test('3rd test - python interpreter should start', async ({ interpreter }) => { + await interpreter.set('Python'); + }); + + test('4th test - python interpreter should NOT start', async ({ interpreter }) => { + await interpreter.set('Python'); + }); + + test('5th test - r interpreter should start', async ({ interpreter }) => { + await interpreter.set('R'); + }); +}); diff --git a/test/smoke/src/e2e/examples/restart-app.test.ts b/test/smoke/src/e2e/examples/restart-app.test.ts new file mode 100644 index 00000000000..74d18f403bc --- /dev/null +++ b/test/smoke/src/e2e/examples/restart-app.test.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { test } from '../_test.setup'; + +test.describe('App Instance Test', () => { + test('1st test creates app instance at worker scope', async ({ app }) => { + await app.code.driver.wait(1000); + console.log('1st app instance'); + }); + + test('2nd test will reuse app instance', async ({ app }) => { + await app.code.driver.wait(1000); + console.log('still the 1st app instance'); + }); + + test('3rd test will create a new app instance', async ({ restartApp: app }) => { + await app.code.driver.wait(1000); + console.log('2nd app instance'); + }); + + + test.describe('App Instance Test Nested', () => { + test('4th test will reuse 2nd app instance - nesting does not matter', async ({ app }) => { + await app.code.driver.wait(1000); + console.log('still the 2nd app instance'); + }); + + test('5th test will create a new app instance', async ({ restartApp: app }) => { + await app.code.driver.wait(1000); + console.log('3rd app instance'); + }); + }); +}); + diff --git a/test/smoke/src/e2e/examples/retry.test.ts b/test/smoke/src/e2e/examples/retry.test.ts new file mode 100644 index 00000000000..151f1fe2f23 --- /dev/null +++ b/test/smoke/src/e2e/examples/retry.test.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { test, expect } from '../_test.setup'; + +test.describe.configure({ retries: 1 }); + +test('should fail first, then pass on retry', { tag: ['@pr'] }, async ({ app, interpreter }, testInfo) => { + if (testInfo.retry) { + await interpreter.set('Python'); + await app.workbench.positronLayouts.enterLayout('notebook'); + await app.workbench.positronNotebooks.createNewNotebook(); + await app.workbench.positronNotebooks.addCodeToFirstCell('this test should PASS! :tada:'); + + expect(true).toBe(true); + } else { + await app.workbench.positronLayouts.enterLayout('notebook'); + await app.workbench.positronNotebooks.createNewNotebook(); + await app.workbench.positronNotebooks.addCodeToFirstCell('this test should fail!'); + expect(true).toBe(false); + } +}); diff --git a/test/smoke/src/e2e/notebook-create.test.ts b/test/smoke/src/e2e/notebook-create.test.ts new file mode 100644 index 00000000000..db39b26c3c8 --- /dev/null +++ b/test/smoke/src/e2e/notebook-create.test.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { test } from './_test.setup'; + +test.use({ + suiteId: 'notebook-create' +}); + +test.describe('Notebooks', { tag: ['@pr', '@web', '@win'] }, () => { + test.describe.skip('Python Notebooks', () => { + test.beforeEach(async function ({ app, interpreter }) { + await interpreter.set('Python'); + await app.workbench.positronLayouts.enterLayout('notebook'); + await app.workbench.positronNotebooks.createNewNotebook(); + await app.workbench.positronNotebooks.selectInterpreter('Python Environments', process.env.POSITRON_PY_VER_SEL!); + }); + + test.afterEach(async function ({ app }) { + await app.workbench.positronNotebooks.closeNotebookWithoutSaving(); + }); + + test('Python - Basic notebook creation and execution (code) [C628631]', async function ({ app }) { + await app.workbench.positronNotebooks.addCodeToFirstCell('eval("8**2")'); + await app.workbench.positronNotebooks.executeCodeInCell(); + await app.workbench.positronNotebooks.assertCellOutput('64'); + }); + + test('Python - Basic notebook creation and execution (markdown) [C628632]', async function ({ app }) { + const randomText = Math.random().toString(36).substring(7); + + await app.workbench.notebook.insertNotebookCell('markdown'); + await app.workbench.notebook.waitForTypeInEditor(`## ${randomText} `); + await app.workbench.notebook.stopEditingCell(); + await app.workbench.positronNotebooks.assertMarkdownText('h2', randomText); + }); + }); + + test.describe('R Notebooks', () => { + test.beforeEach(async function ({ app, interpreter }) { + await interpreter.set('R'); + await app.workbench.positronLayouts.enterLayout('notebook'); + await app.workbench.positronNotebooks.createNewNotebook(); + await app.workbench.positronNotebooks.selectInterpreter('R Environments', process.env.POSITRON_R_VER_SEL!); + }); + + test.afterEach(async function ({ app }) { + await app.workbench.positronNotebooks.closeNotebookWithoutSaving(); + }); + + test('R - Basic notebook creation and execution (code) [C628629]', async function ({ app }) { + await app.workbench.positronNotebooks.addCodeToFirstCell('eval(parse(text="8**2"))'); + await app.workbench.positronNotebooks.executeCodeInCell(); + await app.workbench.positronNotebooks.assertCellOutput('[1] 64'); + }); + + test('R - Basic notebook creation and execution (markdown) [C628630]', async function ({ app }) { + const randomText = Math.random().toString(36).substring(7); + + await app.workbench.notebook.insertNotebookCell('markdown'); + await app.workbench.notebook.waitForTypeInEditor(`## ${randomText} `); + await app.workbench.notebook.stopEditingCell(); + await app.workbench.positronNotebooks.assertMarkdownText('h2', randomText); + }); + }); +}); + + diff --git a/test/smoke/src/e2e/notebook-variables.test.ts b/test/smoke/src/e2e/notebook-variables.test.ts new file mode 100644 index 00000000000..8f7dfe1c82e --- /dev/null +++ b/test/smoke/src/e2e/notebook-variables.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ +import { test, expect } from './_test.setup'; + +test.use({ + suiteId: 'notebook-variables' +}); + +test.describe('Variables Pane - Notebook', { + tag: ['@pr'] +}, () => { + + test.beforeEach(async ({ app }) => { + await app.workbench.positronLayouts.enterLayout('stacked'); + }); + + test('Python - Verifies Variables pane basic function for notebook [C669188]', async function ({ app, interpreter }) { + await interpreter.set('Python'); + + await app.workbench.positronNotebooks.createNewNotebook(); + await app.workbench.positronNotebooks.selectInterpreter('Python Environments', process.env.POSITRON_PY_VER_SEL!); + await app.workbench.positronNotebooks.addCodeToFirstCell('y = [2, 3, 4, 5]'); + await app.workbench.positronNotebooks.executeCodeInCell(); + + const variablesInterpreter = app.workbench.positronVariables.interpreterLocator; + await expect(variablesInterpreter).toBeVisible(); + await expect(variablesInterpreter).toHaveText('Untitled-1.ipynb'); + + await app.workbench.positronLayouts.enterLayout('fullSizedAuxBar'); + const variablesMap = await app.workbench.positronVariables.getFlatVariables(); + expect(variablesMap.get('y')).toStrictEqual({ value: '[2, 3, 4, 5]', type: 'list [4]' }); + await app.workbench.positronNotebooks.closeNotebookWithoutSaving(); + }); + + test('R - Verifies Variables pane basic function for notebook [C669189]', { tag: ['@web'] }, async function ({ app, interpreter }) { + await interpreter.set('R'); + + await app.workbench.positronNotebooks.createNewNotebook(); + await app.workbench.positronNotebooks.selectInterpreter('R Environments', process.env.POSITRON_R_VER_SEL!); + await app.workbench.positronNotebooks.addCodeToFirstCell('y <- c(2, 3, 4, 5)'); + await app.workbench.positronNotebooks.executeCodeInCell(); + + const variablesInterpreter = app.workbench.positronVariables.interpreterLocator; + await expect(variablesInterpreter).toBeVisible(); + await expect(variablesInterpreter).toHaveText('Untitled-1.ipynb'); + + await app.workbench.positronLayouts.enterLayout('fullSizedAuxBar'); + const variablesMap = await app.workbench.positronVariables.getFlatVariables(); + expect(variablesMap.get('y')).toStrictEqual({ value: '2 3 4 5', type: 'dbl [4]' }); + await app.workbench.positronNotebooks.closeNotebookWithoutSaving(); + }); +}); diff --git a/test/smoke/src/e2e/quarto.test.ts b/test/smoke/src/e2e/quarto.test.ts new file mode 100644 index 00000000000..b031688c710 --- /dev/null +++ b/test/smoke/src/e2e/quarto.test.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (C) 2024 Posit Software, PBC. All rights reserved. + * Licensed under the Elastic License 2.0. See LICENSE.txt for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Application } from '../../../automation'; +import { test, expect } from './_test.setup'; +const path = require('path'); +const fs = require('fs-extra'); + +test.use({ + suiteId: 'quarto' +}); + +test.describe('Quarto #web', () => { + test.beforeAll(async function ({ app }) { + await app.workbench.quickaccess.openFile(path.join(app.workspacePathOrFolder, 'workspaces', 'quarto_basic', 'quarto_basic.qmd')); + }); + + test.afterEach(async function ({ app }) { + await deleteGeneratedFiles(app); + }); + + test('should be able to render html [C842847]', async function ({ app }) { + await renderQuartoDocument(app, 'html'); + await verifyDocumentExists(app, 'html'); + }); + + test('should be able to render docx [C842848]', async function ({ app }) { + await renderQuartoDocument(app, 'docx'); + await verifyDocumentExists(app, 'docx'); + }); + + test('should be able to render pdf (LaTeX) [C842890]', async function ({ app }) { + await renderQuartoDocument(app, 'pdf'); + await verifyDocumentExists(app, 'pdf'); + }); + + test('should be able to render pdf (typst) [C842889]', async function ({ app }) { + await renderQuartoDocument(app, 'typst'); + await verifyDocumentExists(app, 'pdf'); + }); + + test('should be able to generate preview [C842891]', async function ({ app }) { + await app.workbench.quickaccess.runCommand('quarto.preview', { keepOpen: true }); + const viewerFrame = app.workbench.positronViewer.getViewerFrame().frameLocator('iframe'); + + // verify preview displays + expect(await viewerFrame.locator('h1').innerText()).toBe('Diamond sizes'); + }); +}); + + +const renderQuartoDocument = async (app: Application, fileExtension: string) => { + await app.workbench.quickaccess.runCommand('quarto.render.document', { keepOpen: true }); + await app.workbench.quickinput.selectQuickInputElementContaining(fileExtension); +}; + +const verifyDocumentExists = async (app: Application, fileExtension: string) => { + await expect(async () => { + await app.workbench.terminal.waitForTerminalText(buffer => buffer.some(line => line.includes(`Output created: quarto_basic.${fileExtension}`))); + expect(await fileExists(app, `quarto_basic.${fileExtension}`)).toBe(true); + }).toPass(); +}; + +const deleteGeneratedFiles = async (app: Application) => { + const files = ['quarto_basic.pdf', 'quarto_basic.html', 'quarto_basic.docx']; + + for (const file of files) { + const filePath = path.join(app.workspacePathOrFolder, 'workspaces', 'quarto_basic', file); + if (await fs.pathExists(filePath)) { + await fs.remove(filePath); + } + } +}; + +const fileExists = (app: Application, file: String) => { + const filePath = path.join(app.workspacePathOrFolder, 'workspaces', 'quarto_basic', file); + return fs.pathExists(filePath); +}; diff --git a/test/smoke/src/test-runner/logger.ts b/test/smoke/src/test-runner/logger.ts index 8e0a521629c..31b44948838 100644 --- a/test/smoke/src/test-runner/logger.ts +++ b/test/smoke/src/test-runner/logger.ts @@ -17,7 +17,7 @@ const VERBOSE = process.env.VERBOSE === 'true'; * @returns Logger instance */ export function createLogger(logsRootPath: string): Logger { - const logsFileName = `smoke-test-runner.log`; + const logsFileName = `e2e-test-runner.log`; const loggers: Logger[] = []; if (VERBOSE) { @@ -64,7 +64,7 @@ function logToFile(logFilePath: string, message: string): void { * @param err error */ export function logErrorToFile(test: any, err: Error): void { - const LOGS_ROOT_PATH = process.env.LOGS_ROOT_PATH || 'LOGS_ROOT_PATH not set'; + const LOGS_ROOT_PATH = process.env.LOGS_ROOT_PATH || 'LOGS_ROOT_PATH not set logger'; const fileName = path.basename(test.file); const testLogPath = path.join(LOGS_ROOT_PATH, fileName, 'retry.log'); diff --git a/test/smoke/src/test-runner/mocha-runner.ts b/test/smoke/src/test-runner/mocha-runner.ts index b23eea8eb8d..667a5319d65 100644 --- a/test/smoke/src/test-runner/mocha-runner.ts +++ b/test/smoke/src/test-runner/mocha-runner.ts @@ -149,7 +149,7 @@ function getFailureLogs(): string { # '${logPath}'. # # Logs of the smoke test runner are stored into -# 'smoke-test-runner.log' in respective folder. +# 'e2e-test-runner.log' in respective folder. # ############################################# `; diff --git a/test/smoke/src/test-runner/test-hooks.ts b/test/smoke/src/test-runner/test-hooks.ts index d0899c7b631..7c141f85c7b 100644 --- a/test/smoke/src/test-runner/test-hooks.ts +++ b/test/smoke/src/test-runner/test-hooks.ts @@ -13,8 +13,8 @@ export const ROOT_PATH = join(__dirname, '..', '..', '..', '..'); const TEST_DATA_PATH = process.env.TEST_DATA_PATH || 'TEST_DATA_PATH not set'; const WORKSPACE_PATH = process.env.WORKSPACE_PATH || 'WORKSPACE_PATH not set'; const EXTENSIONS_PATH = process.env.EXTENSIONS_PATH || 'EXTENSIONS_PATH not set'; -const LOGS_ROOT_PATH = process.env.LOGS_ROOT_PATH || 'LOGS_ROOT_PATH not set'; -const CRASHES_ROOT_PATH = process.env.CRASHES_ROOT_PATH || 'CRASHES_ROOT_PATH not set'; +const LOGS_ROOT_PATH = process.env.LOGS_ROOT_PATH || 'LOGS_ROOT_PATH not set test-hooks'; +const CRASHES_ROOT_PATH = process.env.CRASHES_ROOT_PATH || 'CRASHES_ROOT_PATH not set test-hooks'; const asBoolean = (value: string | undefined): boolean | undefined => { return value === 'true' ? true : value === 'false' ? false : undefined; diff --git a/test/smoke/src/test-runner/test-setup.ts b/test/smoke/src/test-runner/test-setup.ts index c5a3e8c58a0..73de60a7068 100644 --- a/test/smoke/src/test-runner/test-setup.ts +++ b/test/smoke/src/test-runner/test-setup.ts @@ -9,9 +9,9 @@ const rimraf = require('rimraf'); const mkdirp = require('mkdirp'); import { getBuildElectronPath, getDevElectronPath, Logger } from '../../../automation'; import { createLogger } from './logger'; +import * as os from 'os'; -const ROOT_PATH = process.env.ROOT_PATH || 'ROOT_PATH not set'; -const TEST_DATA_PATH = process.env.TEST_DATA_PATH || 'TEST_DATA_PATH not set'; +const TEST_DATA_PATH = join(os.tmpdir(), 'vscsmoke'); const WEB = process.env.WEB; const REMOTE = process.env.REMOTE; const BUILD = process.env.BUILD; @@ -22,12 +22,12 @@ const BUILD = process.env.BUILD; * 2. initializes the test environment * 3. prepares the test data directory */ -export function prepareTestEnv() { - const logsRootPath = join(ROOT_PATH, '.build', 'logs', 'test-setup'); +export function prepareTestEnv(rootPath = process.env.ROOT_PATH || 'not set') { + const logsRootPath = join(rootPath, '.build', 'logs', 'test-setup'); const logger = createLogger(logsRootPath); try { - initializeTestEnvironment(logger); + initializeTestEnvironment(rootPath, logger); console.log('Test environment setup completed successfully.'); // Disabling this section of code for now. It's used to download a stable version of VSCode @@ -48,7 +48,7 @@ export function prepareTestEnv() { /** * Sets up the test environment for Electron or Web smoke tests. */ -function initializeTestEnvironment(logger: Logger): string | null { +function initializeTestEnvironment(rootPath = process.env.ROOT_PATH || 'not set', logger: Logger): string | null { let version: string | null = null; // @@ -66,7 +66,7 @@ function initializeTestEnvironment(logger: Logger): string | null { } else { testCodePath = getDevElectronPath(); electronPath = testCodePath; - process.env.VSCODE_REPOSITORY = ROOT_PATH; + process.env.VSCODE_REPOSITORY = rootPath; process.env.VSCODE_DEV = '1'; process.env.VSCODE_CLI = '1'; } @@ -97,7 +97,7 @@ function initializeTestEnvironment(logger: Logger): string | null { } if (!testCodeServerPath) { - process.env.VSCODE_REPOSITORY = ROOT_PATH; + process.env.VSCODE_REPOSITORY = rootPath; process.env.VSCODE_DEV = '1'; process.env.VSCODE_CLI = '1'; diff --git a/test/smoke/src/test-runner/utils.ts b/test/smoke/src/test-runner/utils.ts index 3ed8dc064a9..2a7b0cc3d1f 100644 --- a/test/smoke/src/test-runner/utils.ts +++ b/test/smoke/src/test-runner/utils.ts @@ -8,41 +8,40 @@ import * as fs from 'fs'; const rimraf = require('rimraf'); const TEST_REPO = process.env.TEST_REPO; -const WORKSPACE_PATH = process.env.WORKSPACE_PATH || 'WORKSPACE_PATH is not set'; /** * Clones or copies the test repository based on options. */ -export function cloneTestRepo() { +export function cloneTestRepo(workspacePath = process.env.WORKSPACE_PATH || 'WORKSPACE_PATH is not set') { const testRepoUrl = 'https://github.com/posit-dev/qa-example-content.git'; if (TEST_REPO) { console.log('Copying test project repository from:', TEST_REPO); // Remove the existing workspace path if the option is provided - rimraf.sync(WORKSPACE_PATH); + rimraf.sync(workspacePath); // Copy the repository based on the platform (Windows vs. non-Windows) if (process.platform === 'win32') { - cp.execSync(`xcopy /E "${TEST_REPO}" "${WORKSPACE_PATH}\\*"`); + cp.execSync(`xcopy /E "${TEST_REPO}" "${workspacePath}\\*"`); } else { - cp.execSync(`cp -R "${TEST_REPO}" "${WORKSPACE_PATH}"`); + cp.execSync(`cp -R "${TEST_REPO}" "${workspacePath}"`); } } else { // If no test-repo is specified, clone the repository if it doesn't exist - if (!fs.existsSync(WORKSPACE_PATH)) { + if (!fs.existsSync(workspacePath)) { console.log('Cloning test project repository from:', testRepoUrl); - const res = cp.spawnSync('git', ['clone', testRepoUrl, WORKSPACE_PATH], { stdio: 'inherit' }); + const res = cp.spawnSync('git', ['clone', testRepoUrl, workspacePath], { stdio: 'inherit' }); // Check if cloning failed by verifying if the workspacePath was created - if (!fs.existsSync(WORKSPACE_PATH)) { + if (!fs.existsSync(workspacePath)) { throw new Error(`Clone operation failed: ${res.stderr?.toString()}`); } } else { console.log('Cleaning and updating test project repository...'); // Fetch the latest changes, reset to the latest commit, and clean the repo - cp.spawnSync('git', ['fetch'], { cwd: WORKSPACE_PATH, stdio: 'inherit' }); - cp.spawnSync('git', ['reset', '--hard', 'FETCH_HEAD'], { cwd: WORKSPACE_PATH, stdio: 'inherit' }); - cp.spawnSync('git', ['clean', '-xdf'], { cwd: WORKSPACE_PATH, stdio: 'inherit' }); + cp.spawnSync('git', ['fetch'], { cwd: workspacePath, stdio: 'inherit' }); + cp.spawnSync('git', ['reset', '--hard', 'FETCH_HEAD'], { cwd: workspacePath, stdio: 'inherit' }); + cp.spawnSync('git', ['clean', '-xdf'], { cwd: workspacePath, stdio: 'inherit' }); } } } diff --git a/test/smoke/yarn.lock b/test/smoke/yarn.lock index fb5810ee80e..0af6794ac3a 100644 --- a/test/smoke/yarn.lock +++ b/test/smoke/yarn.lock @@ -2,6 +2,18 @@ # yarn lockfile v1 +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@mapbox/node-pre-gyp@^1.0.0": version "1.0.11" resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" @@ -17,6 +29,11 @@ semver "^7.3.5" tar "^6.1.11" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@types/events@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" @@ -93,6 +110,13 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -105,6 +129,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -112,11 +141,49 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + "aproba@^1.0.3 || ^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +archiver-utils@^5.0.0, archiver-utils@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-5.0.2.tgz#63bc719d951803efc72cf961a56ef810760dd14d" + integrity sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA== + dependencies: + glob "^10.0.0" + graceful-fs "^4.2.0" + is-stream "^2.0.1" + lazystream "^1.0.0" + lodash "^4.17.15" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + +archiver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-7.0.1.tgz#c9d91c350362040b8927379c7aa69c0655122f61" + integrity sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ== + dependencies: + archiver-utils "^5.0.2" + async "^3.2.4" + buffer-crc32 "^1.0.0" + readable-stream "^4.0.0" + readdir-glob "^1.1.2" + tar-stream "^3.0.0" + zip-stream "^6.0.1" + are-we-there-yet@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" @@ -125,16 +192,36 @@ are-we-there-yet@^2.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +async@^3.2.4: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +b4a@^1.6.4: + version "1.6.7" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.7.tgz#a99587d4ebbfbd5a6e3b21bdb5d5fa385767abe4" + integrity sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg== + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +bare-events@^2.2.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.0.tgz#305b511e262ffd8b9d5616b056464f8e1b3329cc" + integrity sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -143,6 +230,26 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +buffer-crc32@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" + integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w== + +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + call-bind@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" @@ -181,11 +288,23 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + color-support@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" @@ -198,6 +317,17 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +compress-commons@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-6.0.2.tgz#26d31251a66b9d6ba23a84064ecd3a6a71d2609e" + integrity sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg== + dependencies: + crc-32 "^1.2.0" + crc32-stream "^6.0.0" + is-stream "^2.0.1" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -208,6 +338,24 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +crc32-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-6.0.0.tgz#8529a3868f8b27abb915f6c3617c0fadedbf9430" + integrity sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g== + dependencies: + crc-32 "^1.2.0" + readable-stream "^4.0.0" + cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -219,6 +367,15 @@ cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + debug@4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" @@ -255,11 +412,21 @@ detect-libc@^2.0.0: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -299,6 +466,16 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + exec-sh@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.2.tgz#2a5e7ffcbd7d0ba2755bdecb16e5a427dfbdec36" @@ -306,6 +483,19 @@ exec-sh@^0.2.0: dependencies: merge "^1.2.0" +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -356,6 +546,18 @@ get-intrinsic@^1.0.0: has "^1.0.3" has-symbols "^1.0.1" +glob@^10.0.0: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.1.3: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -373,6 +575,11 @@ graceful-fs@^4.1.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== +graceful-fs@^4.2.0: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -408,6 +615,11 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -416,7 +628,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -465,6 +677,11 @@ is-regex@^1.1.1: dependencies: has-symbols "^1.0.1" +is-stream@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -472,16 +689,37 @@ is-symbol@^1.0.2: dependencies: has-symbols "^1.0.1" +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -492,6 +730,16 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" +lodash@^4.17.15: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + make-dir@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -533,6 +781,20 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -550,6 +812,11 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -607,6 +874,11 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + npm-run-all@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" @@ -664,6 +936,11 @@ once@^1.3.0, once@^1.3.1: dependencies: wrappy "1" +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -682,11 +959,24 @@ path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -704,6 +994,21 @@ pify@^3.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -713,6 +1018,19 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" +readable-stream@^2.0.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" @@ -722,6 +1040,24 @@ readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readdir-glob@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== + dependencies: + minimatch "^5.1.0" + resemblejs@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resemblejs/-/resemblejs-5.0.0.tgz#f5a0c6aaa59dcfb9f5192e7ab8740616cbbbf220" @@ -744,6 +1080,11 @@ rimraf@3.0.2, rimraf@^3.0.2: dependencies: glob "^7.1.3" +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -776,11 +1117,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shell-quote@^1.6.1: version "1.7.3" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" @@ -791,6 +1144,11 @@ signal-exit@^3.0.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -831,7 +1189,18 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: +streamx@^2.15.0: + version "2.20.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.20.1.tgz#471c4f8b860f7b696feb83d5b125caab2fdbb93c" + integrity sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -840,6 +1209,24 @@ spdx-license-ids@^3.0.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.padend@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.1.tgz#824c84265dbac46cade2b957b38b6a5d8d1683c5" @@ -865,20 +1252,41 @@ string.prototype.trimstart@^1.0.1: call-bind "^1.0.0" define-properties "^1.1.3" -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" -strip-ansi@^6.0.1: +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -891,6 +1299,15 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +tar-stream@^3.0.0: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + tar@^6.1.11: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" @@ -903,6 +1320,11 @@ tar@^6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" +text-decoder@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.1.tgz#e173f5121d97bfa3ff8723429ad5ba92e1ead67e" + integrity sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -913,7 +1335,7 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -util-deprecate@^1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -954,6 +1376,13 @@ which@^1.2.9: dependencies: isexe "^2.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@^1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -961,6 +1390,24 @@ wide-align@^1.1.2: dependencies: string-width "^1.0.2 || 2 || 3 || 4" +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -970,3 +1417,12 @@ yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +zip-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-6.0.1.tgz#e141b930ed60ccaf5d7fa9c8260e0d1748a2bbfb" + integrity sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA== + dependencies: + archiver-utils "^5.0.0" + compress-commons "^6.0.2" + readable-stream "^4.0.0" diff --git a/yarn.lock b/yarn.lock index ffb732c7ed9..bed968d3c11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -916,12 +916,12 @@ dependencies: playwright-core "1.46.0" -"@playwright/test@^1.46.1": - version "1.46.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.46.1.tgz#a8dfdcd623c4c23bb1b7ea588058aad41055c188" - integrity sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA== +"@playwright/test@^1.48.0": + version "1.48.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.48.0.tgz#4b81434a3ca75e2a6f82a645287784223a45434c" + integrity sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w== dependencies: - playwright "1.46.1" + playwright "1.48.0" "@sindresorhus/is@^4.0.0": version "4.6.0" @@ -8885,17 +8885,17 @@ playwright-core@1.46.0: resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.46.0.tgz#2336ac453a943abf0dc95a76c117f9d3ebd390eb" integrity sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A== -playwright-core@1.46.1: - version "1.46.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.46.1.tgz#28f3ab35312135dda75b0c92a3e5c0e7edb9cc8b" - integrity sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A== +playwright-core@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.48.0.tgz#34d209dd4aba8fccd4a96116f1c4f7630f868722" + integrity sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA== -playwright@1.46.1: - version "1.46.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.46.1.tgz#ea562bc48373648e10420a10c16842f0b227c218" - integrity sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng== +playwright@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.48.0.tgz#00855d9a25f1991d422867f1c32af5d90f457b48" + integrity sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA== dependencies: - playwright-core "1.46.1" + playwright-core "1.48.0" optionalDependencies: fsevents "2.3.2"