diff --git a/circle.yml b/circle.yml index eb571aaa034c..5f8107fe2b77 100644 --- a/circle.yml +++ b/circle.yml @@ -37,6 +37,7 @@ macWorkflowFilters: &darwin-workflow-filters or: - equal: [ develop, << pipeline.git.branch >> ] - equal: [ linux-arm64, << pipeline.git.branch >> ] + - equal: [ 'tbiethman/UNIFY-1816-prototype', << pipeline.git.branch >> ] - matches: pattern: "lmiller/21381-react-18-with-ignore" value: << pipeline.git.branch >> @@ -58,16 +59,16 @@ linuxWorkflowExcludeFilters: &linux-x64-workflow-exclude-filters - false # - equal: [ 'tgriesser/chore/fix-windows-build', << pipeline.git.branch >> ] -# windows is slow in CI, so we only run a certain set of tests on each commit +# windows is slow and expensive in CI, so it normally only runs on main branches # add your branch to this list to run the full Windows build on your PR -fullWindowsWorkflowFilters: &full-windows-workflow-filters - filters: - branches: - only: - - develop - - 'linux-arm64' - - '*-release' - - 'win*' +windowsWorkflowFilters: &windows-workflow-filters + when: + or: + - equal: [ develop, << pipeline.git.branch >> ] + - equal: [ linux-arm64, << pipeline.git.branch >> ] + - matches: + pattern: "-release$" + value: << pipeline.git.branch >> executors: # the Docker image with Cypress dependencies and Chrome browser @@ -537,7 +538,7 @@ commands: DEBUG=<> \ CYPRESS_KONFIG_ENV=production \ CYPRESS_RECORD_KEY=${TEST_LAUNCHPAD_RECORD_KEY:-$MAIN_RECORD_KEY} \ - PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \ + PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \ PERCY_ENABLE=${PERCY_TOKEN:-0} \ PERCY_PARALLEL_TOTAL=-1 \ $cmd yarn workspace @packages/<> cypress:run:<> --browser <> --record --parallel --group <>-<> @@ -562,7 +563,7 @@ commands: DEBUG=<> \ CYPRESS_KONFIG_ENV=production \ - PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \ + PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \ PERCY_ENABLE=${PERCY_TOKEN:-0} \ PERCY_PARALLEL_TOTAL=-1 \ yarn workspace @packages/<> cypress:run:<> --browser <> --spec $TESTFILES @@ -570,7 +571,7 @@ commands: - run: command: | if [[ <> == 'app' && <> == 'true' && -d "packages/app/cypress/screenshots/runner/screenshot/screenshot.cy.tsx/percy" ]]; then - PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \ + PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \ PERCY_ENABLE=${PERCY_TOKEN:-0} \ PERCY_PARALLEL_TOTAL=-1 \ yarn percy upload packages/app/cypress/screenshots/runner/screenshot/screenshot.cy.tsx/percy @@ -1213,7 +1214,7 @@ jobs: run-vite-dev-server-integration-tests - run: command: | - PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \ + PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \ yarn percy build:finalize cli-visual-tests: @@ -1235,7 +1236,7 @@ jobs: - run: name: Upload CLI snapshots for diffing command: | - PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \ + PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \ PERCY_ENABLE=${PERCY_TOKEN:-0} \ PERCY_PARALLEL_TOTAL=-1 \ yarn percy snapshot ./cli/visual-snapshots @@ -1580,7 +1581,7 @@ jobs: command: | CYPRESS_KONFIG_ENV=production \ CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY \ - PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \ + PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \ PERCY_ENABLE=${PERCY_TOKEN:-0} \ PERCY_PARALLEL_TOTAL=-1 \ yarn percy exec --parallel -- -- \ @@ -1603,7 +1604,7 @@ jobs: command: | CYPRESS_KONFIG_ENV=production \ CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY \ - PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \ + PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \ PERCY_ENABLE=${PERCY_TOKEN:-0} \ PERCY_PARALLEL_TOTAL=-1 \ yarn percy exec --parallel -- -- \ @@ -1625,7 +1626,7 @@ jobs: command: | CYPRESS_KONFIG_ENV=production \ CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY \ - PERCY_PARALLEL_NONCE=$CIRCLE_SHA1 \ + PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \ PERCY_ENABLE=${PERCY_TOKEN:-0} \ PERCY_PARALLEL_TOTAL=-1 \ yarn percy exec --parallel -- -- \ @@ -2671,14 +2672,12 @@ windows-workflow: &windows-workflow - windows-build - lint: - <<: *full-windows-workflow-filters name: windows-lint executor: windows requires: - windows-build - unit-tests: - <<: *full-windows-workflow-filters name: windows-unit-tests executor: windows resource_class: windows.large @@ -2686,7 +2685,6 @@ windows-workflow: &windows-workflow - windows-build - create-build-artifacts: - <<: *full-windows-workflow-filters name: windows-create-build-artifacts executor: windows resource_class: windows.large @@ -2717,3 +2715,4 @@ workflows: <<: *darwin-workflow-filters windows: <<: *windows-workflow + <<: *windows-workflow-filters diff --git a/cli/types/cypress.d.ts b/cli/types/cypress.d.ts index 334db2f7cb69..6a36bc3f89a8 100644 --- a/cli/types/cypress.d.ts +++ b/cli/types/cypress.d.ts @@ -2941,7 +2941,7 @@ declare namespace Cypress { /** * Hosts mappings to IP addresses. */ - hosts: null | string[] + hosts: null | {[key: string]: string} /** * Whether Cypress was launched via 'cypress open' (interactive mode) */ diff --git a/npm/mount-utils/package.json b/npm/mount-utils/package.json index 5dc38055b8ba..c135296d772a 100644 --- a/npm/mount-utils/package.json +++ b/npm/mount-utils/package.json @@ -17,6 +17,7 @@ "files": [ "dist" ], + "types": "dist/index.d.ts", "license": "MIT", "repository": { "type": "git", diff --git a/npm/vite-dev-server/src/constants.ts b/npm/vite-dev-server/src/constants.ts index 48b5e73a764c..bed23030e9ce 100644 --- a/npm/vite-dev-server/src/constants.ts +++ b/npm/vite-dev-server/src/constants.ts @@ -3,4 +3,6 @@ export const configFiles = [ 'vite.config.js', 'vite.config.mjs', 'vite.config.cjs', + 'vite.config.mts', + 'vite.config.cts', ] diff --git a/npm/vue/README.md b/npm/vue/README.md index 082e8aa7975f..8fea1bb743e1 100644 --- a/npm/vue/README.md +++ b/npm/vue/README.md @@ -19,6 +19,7 @@ This package allows you to use the [Cypress](https://www.cypress.io/) test runne It uses [Vue Test Utils](https://github.com/vuejs/vue-test-utils) under the hood. This is more of a replacement for node-based testing than it is replacing Vue Test Utils and its API. Instead of running your tests in node (using Jest or Mocha), the Cypress Component Testing Library runs each component in the **real browser** with full power of the Cypress Framework: [live GUI, full API, screen recording, CI support, cross-platform](https://www.cypress.io/features/). One benefit to using Cypress instead of a node-based runner is that limitations of Vue Test Utils in Node (e.g. manually awaiting Vue's internal event loop) are hidden from the user due to Cypress's retry-ability logic. - If you like using `@testing-library/vue`, you can use `@testing-library/cypress` for the same `findBy`, `queryBy` commands, see one of the examples in the list below +- If you need to access the underlying Vue Test Utils API, you can do so by importing it: `import { VueTestUtils } from 'cypress/vue'`. ### How is this different from @cypress/vue2? Cypress packages the current version of Vue under @cypress/vue, and older versions under separate package names. Use [@cypress/vue2](cypress-vue2-npm-url) if you're still using vue@2, and this package if you're on vue@3. diff --git a/npm/vue/cypress/component/test-utils-api/TestUtilsApi.cy.ts b/npm/vue/cypress/component/test-utils-api/TestUtilsApi.cy.ts new file mode 100644 index 000000000000..048aa703ab15 --- /dev/null +++ b/npm/vue/cypress/component/test-utils-api/TestUtilsApi.cy.ts @@ -0,0 +1,23 @@ +import { VueTestUtils, mount } from 'cypress/vue' +import { h } from 'vue' +import TestUtilsApi from './TestUtilsApi.vue' + +const greeting = 'This is a globally registered component!' + +describe('VueTestUtils API', () => { + before(() => { + VueTestUtils.config.global.components = { + 'globally-registered-component': { + setup () { + return () => h('h1', greeting) + }, + }, + } + }) + + it('gains access to underlying Vue Test Utils library', () => { + mount(TestUtilsApi) + + cy.get('h1').contains(greeting) + }) +}) diff --git a/npm/vue/cypress/component/test-utils-api/TestUtilsApi.vue b/npm/vue/cypress/component/test-utils-api/TestUtilsApi.vue new file mode 100644 index 000000000000..a4de3a785559 --- /dev/null +++ b/npm/vue/cypress/component/test-utils-api/TestUtilsApi.vue @@ -0,0 +1,3 @@ + diff --git a/npm/vue/inline-types.ts b/npm/vue/inline-types.ts new file mode 100644 index 000000000000..f7ddbd0c6155 --- /dev/null +++ b/npm/vue/inline-types.ts @@ -0,0 +1,69 @@ +import fs from 'fs-extra' +import globby from 'globby' +import path from 'path' + +// We depend on @vue/test-utils and inline this library in the +// @cypress/vue bundle. +// Unfortunately, although rollup can inline libraries like this, +// TypeScript doesn't do the same for d.ts files - it mains imports +// to the original library, which we do not actually ship in the binary. + +// This script copies the `d.ts` files into the `dist` directory +// then modifies the `index.d.ts` to reference the copied type definitions +// instead of the ones from the local node_modules directory that we don't +// actually bundle in the binary. + +function rewriteSourceForInlineTypes (src: string) { + return src + // Need to modify these lines: + // import type { MountingOptions, VueWrapper } from '@vue/test-utils'; + // import * as _VueTestUtils from '@vue/test-utils'; + // to + // import type { MountingOptions, VueWrapper } from './@vue/test-utils'; + // import * as _VueTestUtils from './@vue/test-utils'; + .replaceAll(`'@vue/test-utils';`, `'./@vue/test-utils';`) + + // Need to modify this line: + // config: import("@vue/test-utils/config).GlobalConfigOptions; + // to + // config: import("./@vue/test-utils/config").GlobalConfigOptions; + .replaceAll(`@vue/test-utils/dist/config`, `./@vue/test-utils/config`) +} + +async function inlineTestUtilsTypes () { + console.log('[npm/vue] Inline type definitions for @vue/test-utils and rewriting source') // eslint-disable-line no-console + + // get the directory with the type definitions we want to inline + const vtuDir = path.dirname(require.resolve('@vue/test-utils')) + + // grab the type definitions + const typeDefs = await globby('**/*.d.ts', { cwd: vtuDir }) + + // make a directory for them in npm/vue/dist + const typeDefDir = path.join(__dirname, 'dist', '@vue', 'test-utils') + + await fs.mkdir(typeDefDir, { recursive: true }) + + // copy the type definitions + await Promise.all( + typeDefs.map((tds) => { + const from = path.join(vtuDir, tds) + const to = path.join(typeDefDir, tds) + + return fs.copy(from, to, { recursive: true }) + }), + ) + + const cypressVueMainTypeDef = path.join(__dirname, 'dist', 'index.d.ts') + + // modify index.d.ts to reference relative type definitions instead of ones from + // node_modules + const updateWithRelativeImports = rewriteSourceForInlineTypes( + await fs.readFile(cypressVueMainTypeDef, 'utf-8'), + ) + + // rewrite index.d.ts, now modified to point at local type definitions. + await fs.writeFile(cypressVueMainTypeDef, updateWithRelativeImports) +} + +inlineTestUtilsTypes() diff --git a/npm/vue/package.json b/npm/vue/package.json index de476f30ac71..19460f9eac27 100644 --- a/npm/vue/package.json +++ b/npm/vue/package.json @@ -8,14 +8,12 @@ "cy:open": "node ../../scripts/cypress.js open --component --project ${PWD}", "cy:run": "node ../../scripts/cypress.js run --component --project ${PWD}", "build": "rimraf dist && rollup -c rollup.config.js", - "postbuild": "node ../../scripts/sync-exported-npm-with-cli.js", - "typecheck": "vue-tsc --noEmit", + "postbuild": "node --require @packages/ts/register ./inline-types.ts && node ../../scripts/sync-exported-npm-with-cli.js", + "typecheck": "yarn tsd && vue-tsc --noEmit", "test": "yarn cy:run", + "tsd": "yarn build && yarn tsc -p test-tsd/tsconfig.tsd.json", "watch": "yarn build --watch --watch.exclude ./dist/**/*" }, - "dependencies": { - "@vue/test-utils": "2.0.0-rc.19" - }, "devDependencies": { "@cypress/code-coverage": "3.8.1", "@cypress/mount-utils": "0.0.0-development", @@ -23,9 +21,11 @@ "@rollup/plugin-node-resolve": "^11.1.1", "@vitejs/plugin-vue": "2.3.1", "@vue/compiler-sfc": "3.2.31", + "@vue/test-utils": "2.0.0-rc.19", "axios": "0.21.2", "cypress": "0.0.0-development", "debug": "^4.3.2", + "globby": "^11.0.1", "rollup": "^2.38.5", "rollup-plugin-istanbul": "2.0.1", "rollup-plugin-typescript2": "^0.29.0", @@ -50,7 +50,7 @@ "engines": { "node": ">=8" }, - "types": "dist", + "types": "dist/index.d.ts", "license": "MIT", "repository": { "type": "git", @@ -71,6 +71,10 @@ { "name": "Amir Rustamzadeh", "social": "@amirrustam" + }, + { + "name": "Lachlan Miller", + "social": "@Lachlan19900" } ], "module": "dist/cypress-vue.esm-bundler.js", diff --git a/npm/vue/src/index.ts b/npm/vue/src/index.ts index 8e92a9622e54..e00871f66888 100644 --- a/npm/vue/src/index.ts +++ b/npm/vue/src/index.ts @@ -1,10 +1,24 @@ /* eslint-disable no-redeclare */ /// -import { ComponentPublicInstance, VNodeProps, AllowedComponentProps, - ComponentCustomProps, ExtractPropTypes, ExtractDefaultPropTypes, - Component, DefineComponent, FunctionalComponent, ComputedOptions, - MethodOptions, ComponentOptionsMixin, EmitsOptions, ComponentOptionsWithObjectProps, ComponentPropsOptions, ComponentOptionsWithArrayProps, ComponentOptionsWithoutProps } from 'vue' -import { MountingOptions, VueWrapper, mount as VTUmount } from '@vue/test-utils' +import type { + ComponentPublicInstance, + VNodeProps, + AllowedComponentProps, + ComponentCustomProps, + ExtractPropTypes, + ExtractDefaultPropTypes, + DefineComponent, + FunctionalComponent, + ComputedOptions, + MethodOptions, + ComponentOptionsMixin, + EmitsOptions, + ComponentOptionsWithObjectProps, + ComponentPropsOptions, + ComponentOptionsWithArrayProps, + ComponentOptionsWithoutProps, +} from 'vue' +import type { MountingOptions, VueWrapper } from '@vue/test-utils' import { injectStylesBeforeElement, StyleOptions, @@ -12,6 +26,22 @@ import { setupHooks, } from '@cypress/mount-utils' +import * as _VueTestUtils from '@vue/test-utils' + +const { + // We do not expose the `mount` from VueTestUtils, instead, we wrap it and expose a + // Cypress-compatible `mount` API. + mount: VTUmount, + // We do not expose shallowMount. It doesn't make much sense in the context of Cypress. + // It might be useful for people who like to migrate some Test Utils tests to Cypress, + // so if we decide it is useful to expose, just remove the next line, and it will be + // available on the `VueTestUtils` import. + shallowMount, + ...VueTestUtils +} = _VueTestUtils + +export { VueTestUtils } + const DEFAULT_COMP_NAME = 'unknown' type GlobalMountOptions = Required>['global'] diff --git a/npm/vue/test-tsd/index.d.ts b/npm/vue/test-tsd/index.d.ts new file mode 100644 index 000000000000..61aeaea7a3b2 --- /dev/null +++ b/npm/vue/test-tsd/index.d.ts @@ -0,0 +1,4 @@ +export function expectType(value: T): void +export function expectError(value: T): void +export function expectAssignable(value: T2): void + diff --git a/npm/vue/test-tsd/mount-test.ts b/npm/vue/test-tsd/mount-test.ts new file mode 100644 index 000000000000..d787014f2203 --- /dev/null +++ b/npm/vue/test-tsd/mount-test.ts @@ -0,0 +1,17 @@ +import { expectError, expectType } from './index' +import { mount, VueTestUtils } from '../dist' +import * as VTU from '@vue/test-utils' +import { defineComponent } from 'vue' + +const App = defineComponent({ + template: `
`, +}) + +// Returns Chainable - not the `mount` function from @vue/test-utils +expectType( + mount(App), +) + +// Rewritten relative types match those copied from node_modules +// see npm/vue/inline-types.ts for more info. +expectType(VTU['config']['global']) diff --git a/npm/vue/test-tsd/tsconfig.tsd.json b/npm/vue/test-tsd/tsconfig.tsd.json new file mode 100644 index 000000000000..483139f4f7c4 --- /dev/null +++ b/npm/vue/test-tsd/tsconfig.tsd.json @@ -0,0 +1,16 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "skipLibCheck": true, + "experimentalDecorators": true, + "strictNullChecks": false + }, + "exclude": [ + "../src" + ], + "include": [ + "../dist", + "../test-dts" + ] +} diff --git a/npm/vue2/package.json b/npm/vue2/package.json index 65ea4f013f0a..c98182dac91c 100644 --- a/npm/vue2/package.json +++ b/npm/vue2/package.json @@ -37,7 +37,7 @@ "engines": { "node": ">=8" }, - "types": "dist", + "types": "dist/index.d.ts", "license": "MIT", "repository": { "type": "git", diff --git a/npm/webpack-dev-server/src/devServer.ts b/npm/webpack-dev-server/src/devServer.ts index 6fd1e7ad8300..132d03baf67a 100644 --- a/npm/webpack-dev-server/src/devServer.ts +++ b/npm/webpack-dev-server/src/devServer.ts @@ -25,7 +25,7 @@ export type WebpackDevServerConfig = { webpackConfig?: unknown // Derived from the user's webpack } -const ALL_FRAMEWORKS = ['create-react-app', 'nuxt', 'react', 'vue-cli', 'next', 'vue'] as const +export const ALL_FRAMEWORKS = ['create-react-app', 'nuxt', 'react', 'vue-cli', 'next', 'vue'] as const /** * @internal diff --git a/npm/webpack-dev-server/src/helpers/sourceRelativeWebpackModules.ts b/npm/webpack-dev-server/src/helpers/sourceRelativeWebpackModules.ts index dcda06107e35..34f2e2afe9d4 100644 --- a/npm/webpack-dev-server/src/helpers/sourceRelativeWebpackModules.ts +++ b/npm/webpack-dev-server/src/helpers/sourceRelativeWebpackModules.ts @@ -1,6 +1,6 @@ import Module from 'module' import path from 'path' -import type { WebpackDevServerConfig } from '../devServer' +import type { WebpackDevServerConfig, ALL_FRAMEWORKS } from '../devServer' import debugFn from 'debug' const debug = debugFn('cypress:webpack-dev-server:sourceRelativeWebpackModules') @@ -58,7 +58,18 @@ export const cypressWebpackPath = (config: WebpackDevServerConfig) => { }) } -// Source the users framework from the provided projectRoot. The framework, if available, will server +type FrameworkWebpackMapper = { [Property in typeof ALL_FRAMEWORKS[number]]: string | undefined } + +const frameworkWebpackMapper: FrameworkWebpackMapper = { + 'create-react-app': 'react-scripts', + 'vue-cli': '@vue/cli-service', + 'nuxt': '@nuxt/webpack', + react: undefined, + vue: undefined, + next: 'next', +} + +// Source the users framework from the provided projectRoot. The framework, if available, will serve // as the resolve base for webpack dependency resolution. export function sourceFramework (config: WebpackDevServerConfig): SourcedDependency | null { debug('Framework: Attempting to source framework for %s', config.cypressConfig.projectRoot) @@ -68,10 +79,18 @@ export function sourceFramework (config: WebpackDevServerConfig): SourcedDepende return null } + const sourceOfWebpack = frameworkWebpackMapper[config.framework] + + if (!sourceOfWebpack) { + debug('Not a higher-order framework so webpack dependencies should be resolvable from projectRoot') + + return null + } + const framework = { } as SourcedDependency try { - const frameworkJsonPath = require.resolve(`${config.framework}/package.json`, { + const frameworkJsonPath = require.resolve(`${sourceOfWebpack}/package.json`, { paths: [config.cypressConfig.projectRoot], }) const frameworkPathRoot = path.dirname(frameworkJsonPath) diff --git a/npm/webpack-dev-server/test/handlers/createReactAppHandler.spec.ts b/npm/webpack-dev-server/test/handlers/createReactAppHandler.spec.ts index bcef18318d4d..9741201188cc 100644 --- a/npm/webpack-dev-server/test/handlers/createReactAppHandler.spec.ts +++ b/npm/webpack-dev-server/test/handlers/createReactAppHandler.spec.ts @@ -55,14 +55,18 @@ describe('createReactAppHandler', function () { process.chdir(projectRoot) - const { frameworkConfig: webpackConfig } = createReactAppHandler({ + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = createReactAppHandler({ cypressConfig: { projectRoot } as Cypress.PluginConfigOptions, + framework: 'create-react-app', } as WebpackDevServerConfig) expect(webpackConfig.mode).eq('development') expectEslintModifications(webpackConfig) expectModuleSourceInPlaceModifications(webpackConfig, projectRoot) expectBabelRuleModifications(webpackConfig, projectRoot) + + expect(sourceWebpackModulesResult.framework?.importPath).to.include('react-scripts') + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(4) }) it('sources the config from react-scripts v5', async () => { @@ -70,8 +74,9 @@ describe('createReactAppHandler', function () { process.chdir(projectRoot) - const { frameworkConfig: webpackConfig } = createReactAppHandler({ + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = createReactAppHandler({ cypressConfig: { projectRoot } as Cypress.PluginConfigOptions, + framework: 'create-react-app', } as WebpackDevServerConfig) expect(webpackConfig.mode).eq('development') @@ -79,6 +84,9 @@ describe('createReactAppHandler', function () { expectModuleSourceInPlaceModifications(webpackConfig, projectRoot) expectBabelRuleModifications(webpackConfig, projectRoot) expectReactScriptsFiveModifications(webpackConfig) + + expect(sourceWebpackModulesResult.framework?.importPath).to.include('react-scripts') + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(5) }) it('sources the config from ejected cra', async () => { @@ -86,8 +94,9 @@ describe('createReactAppHandler', function () { process.chdir(projectRoot) - const { frameworkConfig: webpackConfig } = createReactAppHandler({ + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = createReactAppHandler({ cypressConfig: { projectRoot } as Cypress.PluginConfigOptions, + framework: 'create-react-app', } as WebpackDevServerConfig) expect(webpackConfig.mode).eq('development') @@ -95,5 +104,8 @@ describe('createReactAppHandler', function () { expectModuleSourceInPlaceModifications(webpackConfig, projectRoot) expectBabelRuleModifications(webpackConfig, projectRoot) expectReactScriptsFiveModifications(webpackConfig) + + expect(sourceWebpackModulesResult.framework).to.be.null + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(5) }) }) diff --git a/npm/webpack-dev-server/test/handlers/nextHandler.spec.ts b/npm/webpack-dev-server/test/handlers/nextHandler.spec.ts index f5b1c7b9ec9c..1990728996c9 100644 --- a/npm/webpack-dev-server/test/handlers/nextHandler.spec.ts +++ b/npm/webpack-dev-server/test/handlers/nextHandler.spec.ts @@ -31,7 +31,7 @@ describe('nextHandler', function () { process.chdir(projectRoot) - const { frameworkConfig: webpackConfig } = await nextHandler({ + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await nextHandler({ framework: 'next', cypressConfig: { projectRoot } as Cypress.PluginConfigOptions, } as WebpackDevServerConfig) @@ -39,6 +39,9 @@ describe('nextHandler', function () { expectWatchOverrides(webpackConfig) expectPagesDir(webpackConfig, projectRoot) expectWebpackSpan(webpackConfig) + + expect(sourceWebpackModulesResult.webpack.importPath).to.include('next') + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(5) }) it('sources from a next-11 project', async () => { @@ -46,7 +49,7 @@ describe('nextHandler', function () { process.chdir(projectRoot) - const { frameworkConfig: webpackConfig } = await nextHandler({ + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await nextHandler({ framework: 'next', cypressConfig: { projectRoot } as Cypress.PluginConfigOptions, } as WebpackDevServerConfig) @@ -54,6 +57,27 @@ describe('nextHandler', function () { expectWatchOverrides(webpackConfig) expectPagesDir(webpackConfig, projectRoot) expectWebpackSpan(webpackConfig) + + expect(sourceWebpackModulesResult.webpack.importPath).to.include('next') + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(5) + }) + + it('sources from a next-11-webpack-4 project', async () => { + const projectRoot = await scaffoldMigrationProject('next-11-webpack-4') + + process.chdir(projectRoot) + + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await nextHandler({ + framework: 'next', + cypressConfig: { projectRoot, cypressBinaryRoot: __dirname } as Cypress.PluginConfigOptions, + } as WebpackDevServerConfig) + + expectWatchOverrides(webpackConfig) + expectPagesDir(webpackConfig, projectRoot) + expectWebpackSpan(webpackConfig) + + expect(sourceWebpackModulesResult.webpack.importPath).to.include('next') + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(4) }) it('throws if nodeVersion is set to bundled', async () => { diff --git a/npm/webpack-dev-server/test/handlers/nuxtHandler.spec.ts b/npm/webpack-dev-server/test/handlers/nuxtHandler.spec.ts index edba8b9b5a0c..d44b0b0d300f 100644 --- a/npm/webpack-dev-server/test/handlers/nuxtHandler.spec.ts +++ b/npm/webpack-dev-server/test/handlers/nuxtHandler.spec.ts @@ -13,12 +13,16 @@ describe('nuxtHandler', function () { process.chdir(projectRoot) - const { frameworkConfig: webpackConfig } = await nuxtHandler({ + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await nuxtHandler({ cypressConfig: { projectRoot } as Cypress.PluginConfigOptions, + framework: 'nuxt', } as WebpackDevServerConfig) // Verify it's a Vue-specific webpack config by seeing if VueLoader is present. expect(webpackConfig.plugins.find((plug) => plug.constructor.name === 'VueLoader')) expect(webpackConfig.performance).to.be.undefined + + expect(sourceWebpackModulesResult.framework?.importPath).to.include('@nuxt/webpack') + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(4) }) }) diff --git a/npm/webpack-dev-server/test/handlers/vueCliHandler.spec.ts b/npm/webpack-dev-server/test/handlers/vueCliHandler.spec.ts index 11ebd37c3a96..08bc7acdb861 100644 --- a/npm/webpack-dev-server/test/handlers/vueCliHandler.spec.ts +++ b/npm/webpack-dev-server/test/handlers/vueCliHandler.spec.ts @@ -13,12 +13,16 @@ describe('vueCliHandler', function () { process.chdir(projectRoot) - const { frameworkConfig: webpackConfig } = vueCliHandler({ + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = vueCliHandler({ cypressConfig: { projectRoot } as Cypress.PluginConfigOptions, + framework: 'vue-cli', } as WebpackDevServerConfig) // Verify it's a Vue-specific webpack config by seeing if VueLoader is present. expect(webpackConfig.plugins.find((plug) => plug.constructor.name === 'VueLoader')) + + expect(sourceWebpackModulesResult.framework?.importPath).to.include('@vue/cli-service') + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(5) }) it('sources from a @vue/cli-service@4.x project with Vue 2', async () => { @@ -26,11 +30,15 @@ describe('vueCliHandler', function () { process.chdir(projectRoot) - const { frameworkConfig: webpackConfig } = vueCliHandler({ + const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = vueCliHandler({ cypressConfig: { projectRoot } as Cypress.PluginConfigOptions, + framework: 'vue-cli', } as WebpackDevServerConfig) // Verify it's a Vue-specific webpack config by seeing if VueLoader is present. expect(webpackConfig.plugins.find((plug) => plug.constructor.name === 'VueLoader')) + + expect(sourceWebpackModulesResult.framework?.importPath).to.include('@vue/cli-service') + expect(sourceWebpackModulesResult.webpack.majorVersion).eq(4) }) }) diff --git a/packages/app/cypress/e2e/cypress-in-cypress-run-mode.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress-run-mode.cy.ts index 8759f3cce3ef..fbea5cd5b57b 100644 --- a/packages/app/cypress/e2e/cypress-in-cypress-run-mode.cy.ts +++ b/packages/app/cypress/e2e/cypress-in-cypress-run-mode.cy.ts @@ -62,6 +62,34 @@ describe('Cypress In Cypress - run mode', { viewportWidth: 1200 }, () => { cy.percySnapshot() }) + + it('hides reporter when NO_COMMAND_LOG is set in run mode', () => { + cy.scaffoldProject('cypress-in-cypress') + cy.findBrowsers() + cy.openProject('cypress-in-cypress') + cy.startAppServer() + cy.withCtx(async (ctx, o) => { + const config = await ctx.project.getConfig() + + o.sinon.stub(ctx.project, 'getConfig').resolves({ + ...config, + env: { + ...config.env, + NO_COMMAND_LOG: 1, + }, + }) + }) + + cy.visitApp() + simulateRunModeInUI() + cy.contains('dom-content.spec').click() + + cy.contains('http://localhost:4455/cypress/e2e/dom-content.html').should('be.visible') + cy.findByLabelText('Stats').should('not.exist') + cy.findByTestId('specs-list-panel').should('not.be.visible') + cy.findByTestId('reporter-panel').should('not.be.visible') + cy.findByTestId('sidebar').should('not.exist') + }) }) function simulateRunModeInUI () { diff --git a/packages/app/cypress/e2e/cypress-in-cypress.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress.cy.ts index b50001331544..d02745f839d6 100644 --- a/packages/app/cypress/e2e/cypress-in-cypress.cy.ts +++ b/packages/app/cypress/e2e/cypress-in-cypress.cy.ts @@ -222,6 +222,33 @@ describe('Cypress in Cypress', { viewportWidth: 1500, defaultCommandTimeout: 100 cy.get('[data-cy="playground-num-elements"]').contains('1 Match') }) + + it(`hides reporter when NO_COMMAND_LOG is set in open mode for ${testingType}`, () => { + cy.scaffoldProject('cypress-in-cypress') + cy.findBrowsers() + cy.openProject('cypress-in-cypress') + cy.startAppServer() + cy.withCtx(async (ctx, o) => { + const config = await ctx.project.getConfig() + + o.sinon.stub(ctx.project, 'getConfig').resolves({ + ...config, + env: { + ...config.env, + NO_COMMAND_LOG: 1, + }, + }) + }) + + cy.visitApp() + cy.contains('dom-content.spec').click() + + cy.contains('http://localhost:4455/cypress/e2e/dom-content.html').should('be.visible') + cy.findByLabelText('Stats').should('not.exist') + cy.findByTestId('specs-list-panel').should('not.be.visible') + cy.findByTestId('reporter-panel').should('not.be.visible') + cy.findByTestId('sidebar').should('be.visible') + }) }) it('restarts browser if there is a change on the config file affecting the browser', () => { diff --git a/packages/app/cypress/e2e/settings.cy.ts b/packages/app/cypress/e2e/settings.cy.ts index 7df0b4f1ebfd..accb8ddda10e 100644 --- a/packages/app/cypress/e2e/settings.cy.ts +++ b/packages/app/cypress/e2e/settings.cy.ts @@ -1,5 +1,7 @@ import type { SinonStub } from 'sinon' +const SidebarSettingsLinkSelector = '[data-cy="sidebar-link-settings-page"]' + describe('App: Settings', () => { before(() => { cy.scaffoldProject('todos', { timeout: 50 * 1000 }) @@ -12,7 +14,7 @@ describe('App: Settings', () => { it('visits settings page', () => { cy.startAppServer('e2e') cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.get('div[data-cy="app-header-bar"]').should('contain', 'Settings') cy.findByText('Device Settings').should('be.visible') @@ -22,7 +24,7 @@ describe('App: Settings', () => { it('shows a button to log in if user is not connected', () => { cy.startAppServer('e2e') cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Project Settings').click() cy.get('button').contains('Log In') }) @@ -35,7 +37,7 @@ describe('App: Settings', () => { cy.startAppServer('e2e') cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Dashboard Settings').click() cy.findByText('Project ID').should('be.visible') cy.get('[data-cy="code-box"]').should('contain', 'fromCli') @@ -51,7 +53,7 @@ describe('App: Settings', () => { cy.loginUser() cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Dashboard Settings').click() cy.findByText('Record Key').should('be.visible') }) @@ -61,7 +63,7 @@ describe('App: Settings', () => { cy.loginUser() cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Dashboard Settings').click() cy.get('[data-cy="code-box"]').should('contain', '***') cy.get('[aria-label="Record Key Visibility Toggle"]').click() @@ -99,7 +101,7 @@ describe('App: Settings', () => { cy.waitForSpecToFinish() // Wait for the test to pass, so the test is completed cy.get('.passed > .num').should('contain', 1) - cy.findByTestId('sidebar-link-settings-page').click() + cy.get(SidebarSettingsLinkSelector).click() cy.contains('Dashboard Settings').click() // Assert the data is not there before it arrives cy.contains('Record Key').should('not.exist') @@ -139,7 +141,7 @@ describe('App: Settings', () => { cy.loginUser() cy.visitApp() - cy.findByText('Settings').click() + cy.findByTestId('sidebar-link-settings-page').click() cy.findByText('Project Settings').click() cy.get('[data-cy="file-match-indicator"]').contains('2 Matches') cy.get('[data-cy="spec-pattern"]').contains('**/*.cy.{js,jsx,ts,tsx}') @@ -157,7 +159,7 @@ describe('App: Settings', () => { cy.loginUser() cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Project Settings').click() cy.get('[data-cy="file-match-indicator"]').contains('41 Matches') cy.get('[data-cy="spec-pattern"]').contains('tests/**/*') @@ -168,7 +170,7 @@ describe('App: Settings', () => { cy.loginUser() cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Project Settings').click() cy.get('[data-cy="settings-experiments"]').within(() => { cy.validateExternalLink({ @@ -231,7 +233,7 @@ describe('App: Settings', () => { cy.loginUser() cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Project Settings').click() cy.get('[data-cy="config-code"]').contains('{') }) @@ -241,7 +243,7 @@ describe('App: Settings', () => { cy.loginUser() cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Project Settings').click() cy.get('[data-cy="config-legend"]').within(() => { cy.get('.bg-gray-50').contains('default') @@ -284,7 +286,7 @@ describe('App: Settings', () => { cy.loginUser() cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Project Settings').click() cy.get('[data-cy="config-legend"]').within(() => { cy.get('.bg-gray-50').contains('default') @@ -410,7 +412,7 @@ describe('App: Settings without cloud', () => { cy.startAppServer('component') cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Dashboard Settings').click() cy.findByText('Project ID').should('exist') cy.contains('button', 'Log in to the Cypress Dashboard').should('be.visible') @@ -422,7 +424,7 @@ describe('App: Settings without cloud', () => { cy.startAppServer('component') cy.visitApp() - cy.findByText('Settings').click() + cy.get(SidebarSettingsLinkSelector).click() cy.findByText('Project Settings').click() cy.get('[data-cy=config-code]').within(() => { diff --git a/packages/app/cypress/e2e/sidebar_navigation.cy.ts b/packages/app/cypress/e2e/sidebar_navigation.cy.ts index 5fbd5cdd1b36..05824a74ac63 100644 --- a/packages/app/cypress/e2e/sidebar_navigation.cy.ts +++ b/packages/app/cypress/e2e/sidebar_navigation.cy.ts @@ -1,6 +1,6 @@ import type { SinonStub } from 'sinon' -describe('Sidebar Navigation', () => { +describe('Sidebar Navigation', { viewportWidth: 1280 }, () => { context('accessibility', () => { beforeEach(() => { cy.scaffoldProject('todos') diff --git a/packages/app/cypress/e2e/specs_list_e2e.cy.ts b/packages/app/cypress/e2e/specs_list_e2e.cy.ts index 2ec0cc25a7c7..829cd2ab3aac 100644 --- a/packages/app/cypress/e2e/specs_list_e2e.cy.ts +++ b/packages/app/cypress/e2e/specs_list_e2e.cy.ts @@ -29,11 +29,11 @@ describe('App: Spec List (E2E)', () => { it('shows the "Specs" navigation as highlighted in the lefthand nav bar', () => { cy.findByTestId('sidebar').within(() => { - cy.findByText('Specs').should('be.visible') - cy.findByText('Specs').click() + cy.findByTestId('sidebar-link-specs-page').should('be.visible') + cy.findByTestId('sidebar-link-specs-page').click() }) - cy.get('[data-selected="true"]').contains('Specs').should('be.visible') + cy.findByTestId('sidebar-link-specs-page').find('[data-selected="true"]').should('be.visible') }) it('displays the App Top Nav', () => { diff --git a/packages/app/cypress/fixtures/FileChooser.json b/packages/app/cypress/fixtures/FileChooser.json new file mode 100644 index 000000000000..774e1e260206 --- /dev/null +++ b/packages/app/cypress/fixtures/FileChooser.json @@ -0,0 +1,402 @@ +[ + { + "id": "7892bc4d-876d-421f-8414-61122799331c", + "baseName": "BaseFooterLayout_spec.js", + "relative": "tests/BaseFooterLayout_spec.js", + "absolute": "/home/user/dir/tests/BaseFooterLayout_spec.js", + "name": "BaseFooterLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "BaseFooterLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "9527537a-a7c8-4a69-8f91-22bdf883ef55", + "author": "Kenton_Gerlach", + "lastModifiedHumanReadable": "7 days ago", + "lastModifiedTimestamp": "Fri, 03 Jun 2022 18:48:14 GMT" + } + }, + { + "id": "4fc8d1f5-dd86-430b-b61e-fff8cb9bc72f", + "baseName": "SpecLayout_spec.js", + "relative": "src/components/Skeleton/SpecLayout_spec.js", + "absolute": "/media/src/components/Skeleton/SpecLayout_spec.js", + "name": "SpecLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "SpecLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "6855c0ef-b788-4e58-8966-deb06b05c345", + "author": "Kristoffer36", + "lastModifiedHumanReadable": "9 months ago", + "lastModifiedTimestamp": "Mon, 30 Aug 2021 21:14:42 GMT" + } + }, + { + "id": "e6d568af-5165-4f0a-bfac-336ea9ac28ef", + "baseName": "BaseFooterLayout_spec.js", + "relative": "__test__/BaseFooterLayout_spec.js", + "absolute": "/usr/libexec/__test__/BaseFooterLayout_spec.js", + "name": "BaseFooterLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "BaseFooterLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "927d6b9c-1853-43ec-9c58-4eee6f8e90db", + "author": "Linda83", + "lastModifiedHumanReadable": "5 months ago", + "lastModifiedTimestamp": "Wed, 19 Jan 2022 21:11:53 GMT" + } + }, + { + "id": "6b43e271-d430-40a1-8228-1e4c3844708d", + "baseName": "BaseTableLayout_spec.js", + "relative": "src/components/Table/BaseTableLayout_spec.js", + "absolute": "/usr/ports/src/components/Table/BaseTableLayout_spec.js", + "name": "BaseTableLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "BaseTableLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "4f20835a-223d-4d3b-b0a9-c40d38b0100e", + "author": "Flo_Hudson", + "lastModifiedHumanReadable": "6 months ago", + "lastModifiedTimestamp": "Mon, 13 Dec 2021 17:55:54 GMT" + } + }, + { + "id": "560a730e-4609-4649-9ff6-753d677b8c85", + "baseName": "LazyPersonSkeleton_spec.js", + "relative": "frontend/LazyPersonSkeleton_spec.js", + "absolute": "/boot/frontend/LazyPersonSkeleton_spec.js", + "name": "LazyPersonSkeleton_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "LazyPersonSkeleton", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "b2272b0c-5c08-4df3-9a7a-bed26fd47bb7", + "author": "Addison82", + "lastModifiedHumanReadable": "7 days ago", + "lastModifiedTimestamp": "Fri, 03 Jun 2022 18:33:58 GMT" + } + }, + { + "id": "d40316ae-a9ce-47bd-868f-3c066598e6ac", + "baseName": "VSkeleton_spec.js", + "relative": "__test__/VSkeleton_spec.js", + "absolute": "/usr/ports/__test__/VSkeleton_spec.js", + "name": "VSkeleton_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "VSkeleton", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "aebf5d92-6513-41d3-8d54-92c1d53cb6cc", + "author": "Alphonso86", + "lastModifiedHumanReadable": "a month ago", + "lastModifiedTimestamp": "Wed, 04 May 2022 01:10:29 GMT" + } + }, + { + "id": "fb2bfc05-639a-4262-a2a0-60d0662fc27b", + "baseName": "LogoutLayout_spec.js", + "relative": "__test__/LogoutLayout_spec.js", + "absolute": "/Library/__test__/LogoutLayout_spec.js", + "name": "LogoutLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "LogoutLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "5bafa33c-cc71-42d4-bb26-864b2f133366", + "author": "Lucious85", + "lastModifiedHumanReadable": "24 minutes ago", + "lastModifiedTimestamp": "Fri, 10 Jun 2022 07:29:48 GMT" + } + }, + { + "id": "5b6e0f2a-87e1-428b-a38d-91d559f7f5df", + "baseName": "VirtualLayout_spec.js", + "relative": "src/views/VirtualLayout_spec.js", + "absolute": "/usr/X11R6/src/views/VirtualLayout_spec.js", + "name": "VirtualLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "VirtualLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "8b6e0f4d-aa01-4390-9026-e4d38b73734b", + "author": "Griffin.Fritsch", + "lastModifiedHumanReadable": "3 days ago", + "lastModifiedTimestamp": "Tue, 07 Jun 2022 12:33:52 GMT" + } + }, + { + "id": "35a1739f-652b-4522-8de6-e04df3ae8467", + "baseName": "CyDynamicProductLayout_spec.js", + "relative": "frontend/CyDynamicProductLayout_spec.js", + "absolute": "/Network/frontend/CyDynamicProductLayout_spec.js", + "name": "CyDynamicProductLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "CyDynamicProductLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "ffdb38b0-731f-4885-9d0b-454b2856341c", + "author": "Ayana_Russel", + "lastModifiedHumanReadable": "a day ago", + "lastModifiedTimestamp": "Thu, 09 Jun 2022 06:41:51 GMT" + } + }, + { + "id": "57e654a5-33e4-4d1d-baac-4bed6f97855a", + "baseName": "LazySpecFooter_spec.js", + "relative": "src/Footer/LazySpecFooter_spec.js", + "absolute": "/var/log/src/Footer/LazySpecFooter_spec.js", + "name": "LazySpecFooter_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "LazySpecFooter", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "1d0d0fa0-978f-4e24-ba87-aa5742c00fcc", + "author": "Hilbert.Watsica", + "lastModifiedHumanReadable": "a day ago", + "lastModifiedTimestamp": "Wed, 08 Jun 2022 22:03:15 GMT" + } + }, + { + "id": "bd36a331-34eb-49d6-9c31-57fb172db324", + "baseName": "VLazyCell_spec.js", + "relative": "src/views/VLazyCell_spec.js", + "absolute": "/Library/src/views/VLazyCell_spec.js", + "name": "VLazyCell_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "VLazyCell", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "8cc4a9c9-d4a3-4243-a60d-105739948778", + "author": "Elise_Rodriguez", + "lastModifiedHumanReadable": "28 minutes ago", + "lastModifiedTimestamp": "Fri, 10 Jun 2022 07:26:13 GMT" + } + }, + { + "id": "4c70f2bb-8625-4194-96e1-69d094e37ff4", + "baseName": "CellLayout_spec.js", + "relative": "src/Cell/CellLayout_spec.js", + "absolute": "/selinux/src/Cell/CellLayout_spec.js", + "name": "CellLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "CellLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "50abadd1-5ff9-47b9-bff3-a282ff8d890e", + "author": "Karolann.Cremin36", + "lastModifiedHumanReadable": "10 months ago", + "lastModifiedTimestamp": "Tue, 17 Aug 2021 18:21:16 GMT" + } + }, + { + "id": "32867553-a10e-45c6-bf8b-0ac226f58f97", + "baseName": "BaseFooter_spec.js", + "relative": "src/Footer/BaseFooter_spec.js", + "absolute": "/Users/src/Footer/BaseFooter_spec.js", + "name": "BaseFooter_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "BaseFooter", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "d7abe60d-eff5-4201-a559-18464ec93e09", + "author": "Gene_Denesik2", + "lastModifiedHumanReadable": "44 minutes ago", + "lastModifiedTimestamp": "Fri, 10 Jun 2022 07:10:36 GMT" + } + }, + { + "id": "4ab2792a-0988-449d-b5cf-10e47ab76708", + "baseName": "VFooter_spec.js", + "relative": "src/views/VFooter_spec.js", + "absolute": "/Users/src/views/VFooter_spec.js", + "name": "VFooter_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "VFooter", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "c52854b3-54ed-4eea-bc66-ca20bc64d638", + "author": "Rebekah54", + "lastModifiedHumanReadable": "an hour ago", + "lastModifiedTimestamp": "Fri, 10 Jun 2022 06:26:41 GMT" + } + }, + { + "id": "f4fb0753-434b-42a9-92d9-29350d8cd4e1", + "baseName": "VTableLayout_spec.js", + "relative": "src/components/Table/VTableLayout_spec.js", + "absolute": "/private/src/components/Table/VTableLayout_spec.js", + "name": "VTableLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "VTableLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "41470434-031f-4e67-8173-bcd8c8b03255", + "author": "Jadyn23", + "lastModifiedHumanReadable": "6 days ago", + "lastModifiedTimestamp": "Sat, 04 Jun 2022 14:41:22 GMT" + } + }, + { + "id": "6d8277e9-4f44-4524-ab40-49227d3c5257", + "baseName": "AccountLayout_spec.js", + "relative": "src/views/AccountLayout_spec.js", + "absolute": "/System/src/views/AccountLayout_spec.js", + "name": "AccountLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "AccountLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "d74ab465-3f73-48c7-b2a4-1a0cb6173df3", + "author": "Virginie_Monahan58", + "lastModifiedHumanReadable": "8 days ago", + "lastModifiedTimestamp": "Thu, 02 Jun 2022 18:43:09 GMT" + } + }, + { + "id": "59f6d1fb-4f9e-4aa6-89e3-85323af59656", + "baseName": "WizardPage_spec.js", + "relative": "src/views/WizardPage_spec.js", + "absolute": "/mnt/src/views/WizardPage_spec.js", + "name": "WizardPage_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "WizardPage", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "07e7bd54-ecad-4f97-91e1-bb19f31858ed", + "author": "Lindsay.Becker82", + "lastModifiedHumanReadable": "5 months ago", + "lastModifiedTimestamp": "Wed, 12 Jan 2022 15:33:20 GMT" + } + }, + { + "id": "6329076c-3015-4773-93ab-87d2744e5f3a", + "baseName": "PersonLayout_spec.js", + "relative": "frontend/components/PersonLayout_spec.js", + "absolute": "/var/log/frontend/components/PersonLayout_spec.js", + "name": "PersonLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "PersonLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "4cb5b7b1-49cb-4033-b0ac-7c8338c91070", + "author": "Einar11", + "lastModifiedHumanReadable": "an hour ago", + "lastModifiedTimestamp": "Fri, 10 Jun 2022 07:04:19 GMT" + } + }, + { + "id": "a9f4b7a8-67e5-464d-af8c-b086487ed0e7", + "baseName": "IFooterLayout_spec.js", + "relative": "frontend/components/IFooterLayout_spec.js", + "absolute": "/var/mail/frontend/components/IFooterLayout_spec.js", + "name": "IFooterLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "IFooterLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "a3d5616f-3a7b-4d6b-bb68-4a82d8db4128", + "author": "Misty.Batz", + "lastModifiedHumanReadable": "2 months ago", + "lastModifiedTimestamp": "Tue, 29 Mar 2022 06:11:00 GMT" + } + }, + { + "id": "e10063e7-92b1-4976-a80f-3eeaa8d1ee32", + "baseName": "VSettingsButtonList.spec.tsx", + "relative": "tests/VSettingsButtonList.spec.tsx", + "absolute": "/usr/lib/tests/VSettingsButtonList.spec.tsx", + "name": "VSettingsButtonList.spec", + "specFileExtension": ".spec.tsx", + "fileExtension": ".tsx", + "specType": "component", + "fileName": "VSettingsButtonList", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "241f3f02-1c90-4399-b198-c0d18e1ae20e", + "author": "Penelope43", + "lastModifiedHumanReadable": "3 days ago", + "lastModifiedTimestamp": "Tue, 07 Jun 2022 09:03:24 GMT" + } + } +] \ No newline at end of file diff --git a/packages/app/cypress/fixtures/FileList.json b/packages/app/cypress/fixtures/FileList.json new file mode 100644 index 000000000000..c5bba1c69213 --- /dev/null +++ b/packages/app/cypress/fixtures/FileList.json @@ -0,0 +1,202 @@ +[ + { + "id": "1474a9d8-a278-492b-84d8-76d21f841461", + "baseName": "SpecPage_spec.js", + "relative": "src/views/SpecPage_spec.js", + "absolute": "/etc/src/views/SpecPage_spec.js", + "name": "SpecPage_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "SpecPage", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "22799331-c395-4275-b7aa-7c8a69cf9122", + "author": "May_Stehr50", + "lastModifiedHumanReadable": "2 months ago", + "lastModifiedTimestamp": "Sun, 27 Mar 2022 15:26:50 GMT" + } + }, + { + "id": "946c65c7-84fc-48d1-b5dd-8630b361efff", + "baseName": "SettingsCellLayout_spec.js", + "relative": "src/Cell/SettingsCellLayout_spec.js", + "absolute": "/proc/src/Cell/SettingsCellLayout_spec.js", + "name": "SettingsCellLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "SettingsCellLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "cb9bc72f-4685-45c0-afb7-88e584966deb", + "author": "Abner.Hoeger35", + "lastModifiedHumanReadable": "7 days ago", + "lastModifiedTimestamp": "Fri, 03 Jun 2022 05:22:54 GMT" + } + }, + { + "id": "e05a86c7-e6d5-468a-b516-5f0abfac336e", + "baseName": "CyFooterLayout_spec.js", + "relative": "src/Footer/CyFooterLayout_spec.js", + "absolute": "/tmp/src/Footer/CyFooterLayout_spec.js", + "name": "CyFooterLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "CyFooterLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "9ac28efc-927d-46b9-8185-33ec9c584eee", + "author": "Heber_Wolff", + "lastModifiedHumanReadable": "3 months ago", + "lastModifiedTimestamp": "Fri, 04 Mar 2022 00:19:24 GMT" + } + }, + { + "id": "e2d98758-6b43-4e27-9d43-00a142281e4c", + "baseName": "BaseFooterLayout_spec.js", + "relative": "src/views/BaseFooterLayout_spec.js", + "absolute": "/home/user/src/views/BaseFooterLayout_spec.js", + "name": "BaseFooterLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "BaseFooterLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "844708dd-4f20-4835-a223-dd3bb0a9c40d", + "author": "Daphney.Lockman8", + "lastModifiedHumanReadable": "10 months ago", + "lastModifiedTimestamp": "Thu, 12 Aug 2021 18:13:16 GMT" + } + }, + { + "id": "9e0d1105-60a7-430e-8609-6499ff6753d6", + "baseName": "VStaticWizardTable_spec.js", + "relative": "src/components/Table/VStaticWizardTable_spec.js", + "absolute": "/private/var/src/components/Table/VStaticWizardTable_spec.js", + "name": "VStaticWizardTable_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "VStaticWizardTable", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "7b8c850b-2272-4b0c-9c08-df35a7abed26", + "author": "Ubaldo44", + "lastModifiedHumanReadable": "a year ago", + "lastModifiedTimestamp": "Sun, 04 Jul 2021 01:15:24 GMT" + } + }, + { + "id": "0df815ad-4031-46ae-a9ce-7bd868f3c066", + "baseName": "VLazyTable_spec.js", + "relative": "src/views/VLazyTable_spec.js", + "absolute": "/Network/src/views/VLazyTable_spec.js", + "name": "VLazyTable_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "VLazyTable", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "98e6acda-ebf5-4d92-a513-1d3cd5492c1d", + "author": "Erica_DuBuque40", + "lastModifiedHumanReadable": "6 days ago", + "lastModifiedTimestamp": "Sat, 04 Jun 2022 14:44:46 GMT" + } + }, + { + "id": "d6f3cefb-2bfc-4056-b9a2-62e2a060d066", + "baseName": "FooterLayout_spec.js", + "relative": "src/views/FooterLayout_spec.js", + "absolute": "/etc/ppp/src/views/FooterLayout_spec.js", + "name": "FooterLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "FooterLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "fc27b45b-afa3-43cc-8712-d47b26864b2f", + "author": "Arjun20", + "lastModifiedHumanReadable": "6 days ago", + "lastModifiedTimestamp": "Sat, 04 Jun 2022 06:55:17 GMT" + } + }, + { + "id": "db51525b-6e0f-42a8-be12-8b238d91d559", + "baseName": "PersonLayout_spec.js", + "relative": "frontend/components/PersonLayout_spec.js", + "absolute": "/var/yp/frontend/components/PersonLayout_spec.js", + "name": "PersonLayout_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "PersonLayout", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "7f5dfb8b-6e0f-44da-a013-905026e4d38b", + "author": "Jalon.Doyle", + "lastModifiedHumanReadable": "3 days ago", + "lastModifiedTimestamp": "Mon, 06 Jun 2022 22:40:58 GMT" + } + }, + { + "id": "202ef035-a173-49f6-92b5-224de6e04df3", + "baseName": "ProductPage_spec.js", + "relative": "frontend/components/ProductPage_spec.js", + "absolute": "/usr/frontend/components/ProductPage_spec.js", + "name": "ProductPage_spec", + "specFileExtension": "_spec.js", + "fileExtension": ".js", + "specType": "component", + "fileName": "ProductPage", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "e84675ff-db38-4b07-b1f8-855d0b454b28", + "author": "Ewell25", + "lastModifiedHumanReadable": "5 months ago", + "lastModifiedTimestamp": "Fri, 14 Jan 2022 09:03:40 GMT" + } + }, + { + "id": "c12e9357-e654-4a53-be4d-1dbaac4bed6f", + "baseName": "VLazySettingsButton.spec.tsx", + "relative": "tests/VLazySettingsButton.spec.tsx", + "absolute": "/sys/tests/VLazySettingsButton.spec.tsx", + "name": "VLazySettingsButton.spec", + "specFileExtension": ".spec.tsx", + "fileExtension": ".tsx", + "specType": "component", + "fileName": "VLazySettingsButton", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "7855ae1d-0d0f-4a09-b8fe-243a87aa5742", + "author": "Ora94", + "lastModifiedHumanReadable": "a month ago", + "lastModifiedTimestamp": "Thu, 05 May 2022 14:02:11 GMT" + } + } +] \ No newline at end of file diff --git a/packages/app/cypress/fixtures/GeneratorSuccess.json b/packages/app/cypress/fixtures/GeneratorSuccess.json new file mode 100644 index 000000000000..701680d7ef24 --- /dev/null +++ b/packages/app/cypress/fixtures/GeneratorSuccess.json @@ -0,0 +1,22 @@ +[ + { + "id": "6765523b-9147-44a9-98a2-7892bc4d876d", + "baseName": "VSettingsButtonList.spec.tsx", + "relative": "tests/VSettingsButtonList.spec.tsx", + "absolute": "/etc/ppp/tests/VSettingsButtonList.spec.tsx", + "name": "VSettingsButtonList.spec", + "specFileExtension": ".spec.tsx", + "fileExtension": ".tsx", + "specType": "component", + "fileName": "VSettingsButtonList", + "__typename": "FileParts", + "gitInfo": { + "__typename": "GitInfo", + "statusType": "unmodified", + "id": "1f841461-1227-4993-b1c3-9527537aa7c8", + "author": "Makenna_Howell", + "lastModifiedHumanReadable": "an hour ago", + "lastModifiedTimestamp": "Fri, 10 Jun 2022 07:27:34 GMT" + } + } +] \ No newline at end of file diff --git a/packages/app/package.json b/packages/app/package.json index 99d9eebea668..0db3ad18d851 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -76,16 +76,20 @@ "vite": { "optimizeDeps": { "include": [ + "@cypress/parse-domain", "@headlessui/vue", "@iconify/iconify", "@percy/cypress", "@testing-library/cypress/add-commands", + "@toycode/markdown-it-class", "@urql/exchange-execute", "@urql/vue", "@vueuse/core", + "ansi-to-html", "bluebird", "cypress-real-events", "dedent", + "debug", "events", "fake-uuid", "floating-vue", @@ -95,12 +99,15 @@ "graphql/jsutils/Path", "gravatar", "human-interval", + "javascript-time-ago", "lodash", + "markdown-it", "mobx", "nanoid", "pinia", "shiki", "socket.io-client", + "url", "vue", "vue-demi", "vue-toastification", diff --git a/packages/app/src/navigation/SidebarNavigation.cy.tsx b/packages/app/src/navigation/SidebarNavigation.cy.tsx index b41833f004ee..7b914f19e5d5 100644 --- a/packages/app/src/navigation/SidebarNavigation.cy.tsx +++ b/packages/app/src/navigation/SidebarNavigation.cy.tsx @@ -14,7 +14,7 @@ function mountComponent (initialNavExpandedVal = true) { } describe('SidebarNavigation', () => { - it('expands the bar when clicking the expand button', () => { + it('expands the bar when clicking the expand button', { viewportWidth: 1280 }, () => { mountComponent() cy.findByText('test-project').should('not.be.visible') @@ -31,6 +31,32 @@ describe('SidebarNavigation', () => { cy.percySnapshot() }) + it('automatically collapses when viewport decreases < 1024px', () => { + cy.viewport(1280, 1000) + mountComponent() + + // Collapsed by default + cy.findByText('test-project').should('not.be.visible') + + // Expand + cy.findByLabelText(defaultMessages.sidebar.toggleLabel.collapsed, { + selector: 'button', + }).click() + + // Verify expanded - project title should display + cy.findByText('test-project').should('be.visible') + + // Shrink viewport, should collapse + cy.viewport(1000, 1000) + + // Project title should not be visible anymore + cy.findByText('test-project').should('not.be.visible') + // Expand control should not be available + cy.findByLabelText(defaultMessages.sidebar.toggleLabel.collapsed, { + selector: 'button', + }).should('not.exist') + }) + it('shows tooltips on hover', () => { mountComponent(false) cy.findByTestId('sidebar-header').trigger('mouseenter') diff --git a/packages/app/src/navigation/SidebarNavigation.vue b/packages/app/src/navigation/SidebarNavigation.vue index 72d5aaeff79e..da49988da25b 100644 --- a/packages/app/src/navigation/SidebarNavigation.vue +++ b/packages/app/src/navigation/SidebarNavigation.vue @@ -103,6 +103,7 @@ import CypressLogo from '@packages/frontend-shared/src/assets/logos/cypress_s.pn import { useI18n } from '@cy/i18n' import { useRoute } from 'vue-router' import SidebarNavigationHeader from './SidebarNavigationHeader.vue' +import { useWindowSize } from '@vueuse/core' const { t } = useI18n() @@ -140,6 +141,8 @@ query SideBarNavigation { } ` +const NAV_EXPAND_MIN_SCREEN_WIDTH = 1024 + const query = useQuery({ query: SideBarNavigationDocument }) const setPreferences = useMutation(SideBarNavigation_SetPreferencesDocument) @@ -148,12 +151,14 @@ const bindingsOpen = ref(false) const route = useRoute() -const navIsAlwaysCollapsed = computed(() => route.meta?.navBarExpandedAllowed !== false) +const navIsAlwaysCollapsed = computed(() => width.value >= NAV_EXPAND_MIN_SCREEN_WIDTH && route.meta?.navBarExpandedAllowed !== false) const isNavBarExpanded = ref(true) +const { width } = useWindowSize() + watchEffect(() => { - if (route.meta?.navBarExpandedAllowed === false) { + if (width.value < NAV_EXPAND_MIN_SCREEN_WIDTH || route.meta?.navBarExpandedAllowed === false) { isNavBarExpanded.value = false } else { isNavBarExpanded.value = query.data?.value?.localSettings.preferences.isSideNavigationOpen ?? true @@ -161,7 +166,7 @@ watchEffect(() => { }) const toggleNavbarIfAllowed = () => { - if (route.meta?.navBarExpandedAllowed === false) { + if (width.value < NAV_EXPAND_MIN_SCREEN_WIDTH || route.meta?.navBarExpandedAllowed === false) { return } diff --git a/packages/app/src/runner/ResizablePanels.vue b/packages/app/src/runner/ResizablePanels.vue index f4f19ff9e11e..701290afdf14 100644 --- a/packages/app/src/runner/ResizablePanels.vue +++ b/packages/app/src/runner/ResizablePanels.vue @@ -8,6 +8,7 @@ @mouseup="handleMouseup" @mousemove="handleMousemove" > +
- +
@@ -51,6 +51,7 @@ class="h-full" >
{ return props.gql.localSettings.preferences.isSpecsListOpen ?? false }) +const hideCommandLog = runnerUiStore.hideCommandLog + // watch active spec, and re-run if it changes! startSpecWatcher() @@ -210,12 +213,16 @@ onMounted(() => { }) preferences.update('autoScrollingEnabled', props.gql.localSettings.preferences.autoScrollingEnabled ?? true) -preferences.update('isSpecsListOpen', isSpecsListOpenPreferences.value) -preferences.update('reporterWidth', reporterWidthPreferences.value) -preferences.update('specListWidth', specsListWidthPreferences.value) -// 👆 we must update these preferences before calling useRunnerStyle, to make sure that values from GQL +// if the CYPRESS_NO_COMMAND_LOG environment variable is set, +// don't use the widths or the open status of specs list from GraphQL +if (!hideCommandLog) { + preferences.update('isSpecsListOpen', isSpecsListOpenPreferences.value) + preferences.update('reporterWidth', reporterWidthPreferences.value) + preferences.update('specListWidth', specsListWidthPreferences.value) + // 👆 we must update these preferences before calling useRunnerStyle, to make sure that values from GQL // will be available during the initial calculation that useRunnerStyle does +} const { viewportStyle, diff --git a/packages/app/src/runner/SpecRunnerRunMode.vue b/packages/app/src/runner/SpecRunnerRunMode.vue index e2db4b72d633..d50236855ee5 100644 --- a/packages/app/src/runner/SpecRunnerRunMode.vue +++ b/packages/app/src/runner/SpecRunnerRunMode.vue @@ -17,7 +17,7 @@ :initial-panel1-width="0" :initial-panel2-width="runnerUiStore.reporterWidth" :show-panel1="false" - :show-panel2="!screenshotStore.isScreenshotting" + :show-panel2="!screenshotStore.isScreenshotting && !hideCommandLog" @resize-end="handleResizeEnd" @panel-width-updated="handlePanelWidthUpdated" > @@ -26,6 +26,7 @@ class="h-full" >
@@ -9,7 +9,7 @@