diff --git a/.circleci/cache-version.txt b/.circleci/cache-version.txt index 948de64c5a39..4ee70cb0d70c 100644 --- a/.circleci/cache-version.txt +++ b/.circleci/cache-version.txt @@ -1,3 +1,3 @@ # Bump this version to force CI to re-create the cache from scratch. -6-9-2025 +6-30-2025 diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index b1b5da917fdb..e80dc0f07d16 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -38,7 +38,7 @@ mainBuildFilters: &mainBuildFilters - /^release\/\d+\.\d+\.\d+$/ # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - 'update-v8-snapshot-cache-on-develop' - - 'chore/test_cypress_recipes_15' + - 'mabel/issue-31677-reporter-redesign' # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing @@ -49,7 +49,7 @@ macWorkflowFilters: &darwin-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/test_cypress_recipes_15', << pipeline.git.branch >> ] + - equal: [ 'mabel/issue-31677-reporter-redesign', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -60,7 +60,7 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/test_cypress_recipes_15', << pipeline.git.branch >> ] + - equal: [ 'mabel/issue-31677-reporter-redesign', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -83,7 +83,7 @@ windowsWorkflowFilters: &windows-workflow-filters - equal: [ develop, << pipeline.git.branch >> ] # use the following branch as well to ensure that v8 snapshot cache updates are fully tested - equal: [ 'update-v8-snapshot-cache-on-develop', << pipeline.git.branch >> ] - - equal: [ 'chore/test_cypress_recipes_15', << pipeline.git.branch >> ] + - equal: [ 'mabel/issue-31677-reporter-redesign', << pipeline.git.branch >> ] - matches: pattern: /^release\/\d+\.\d+\.\d+$/ value: << pipeline.git.branch >> @@ -157,7 +157,7 @@ commands: name: Set environment variable to determine whether or not to persist artifacts command: | echo "Setting SHOULD_PERSIST_ARTIFACTS variable" - echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "chore/test_cypress_recipes_15" ]]; then + echo 'if ! [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "mabel/issue-31677-reporter-redesign" ]]; then export SHOULD_PERSIST_ARTIFACTS=true fi' >> "$BASH_ENV" # You must run `setup_should_persist_artifacts` command and be using bash before running this command diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 503c9e3eed4c..644503814d40 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -26,6 +26,7 @@ _Released 07/15/2025 (PENDING)_ **Misc:** +- The Cypress Command log has a new design when viewing a list of tests. Addresses [#31677](https://github.com/cypress-io/cypress/issues/31677). Addressed in [#31914](https://github.com/cypress-io/cypress/pull/31914). - Migration helpers and related errors are no longer shown when upgrading from Cypress versions earlier than 10.0.0. To migrate from a pre-10.0.0 version, upgrade one major version at a time to receive the appropriate guidance. Addresses [#31345](https://github.com/cypress-io/cypress/issues/31345). Addressed in [https://github.com/cypress-io/cypress/pull/31629/](https://github.com/cypress-io/cypress/pull/31629/). ## 14.5.1 diff --git a/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts index 14e709847ca9..1396fd34eaac 100644 --- a/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts +++ b/packages/app/cypress/e2e/cypress-in-cypress-e2e.cy.ts @@ -213,7 +213,6 @@ describe('Cypress In Cypress E2E', { viewportWidth: 1500, defaultCommandTimeout: cy.visitApp() cy.specsPageIsVisible() cy.contains('withFailure.spec').click() - cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') cy.get('[data-cy="runnable-header"]').should('be.visible') cy.get('body').type('f') diff --git a/packages/app/cypress/e2e/cypress-in-cypress.cy.ts b/packages/app/cypress/e2e/cypress-in-cypress.cy.ts index ea471e346152..a8399597cec7 100644 --- a/packages/app/cypress/e2e/cypress-in-cypress.cy.ts +++ b/packages/app/cypress/e2e/cypress-in-cypress.cy.ts @@ -131,8 +131,7 @@ describe('Cypress in Cypress', { viewportWidth: 1500, defaultCommandTimeout: 100 // validate that the width we set in `withCtx` above is the starting point cy.get(`[data-cy="reporter-panel"]`).invoke('outerWidth').should('eq', 800) - cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') - .click({ force: true }) + cy.findByTestId('toggle-specs-button').click({ force: true }) // this tooltip text confirms specs list is open cy.contains('Collapse Specs List') diff --git a/packages/app/cypress/e2e/runner/reporter.hooks.cy.ts b/packages/app/cypress/e2e/runner/reporter.hooks.cy.ts index 8b15e7d3f49c..328dc0934813 100644 --- a/packages/app/cypress/e2e/runner/reporter.hooks.cy.ts +++ b/packages/app/cypress/e2e/runner/reporter.hooks.cy.ts @@ -53,11 +53,11 @@ describe('hooks', { o.sinon.stub(ctx.actions.file, 'openFile') }) - cy.contains('Open in IDE').invoke('show').click({ force: true }) + cy.get('.hook-open-in-ide').first().invoke('show').click() cy.withCtx((ctx, o) => { - expect(ctx.actions.file.openFile).to.have.been.calledWith(o.sinon.match(new RegExp(`hooks/basic\.cy\.js$`)), o.ideLine, o.ideColumn) - }, { ideLine: 2, ideColumn: Cypress.browser.family === 'firefox' ? 5 : 2 }) + expect(ctx.actions.file.openFile).to.have.been.calledWith(o.sinon.match(new RegExp(`hooks/basic\.cy\.js$`)), 2, 2) + }) }) it('does not display commands from skipped tests', () => { @@ -67,9 +67,7 @@ describe('hooks', { }) // does not display commands from skipped tests - cy.contains('test 1').click() - cy.contains('test 1').parents('.collapsible').first().should('not.contain', 'testBody 1') - cy.contains('test 1').click() + cy.contains('test 1').should('have.css', 'pointer-events', 'none') // displays before hook when following it.skip // https://github.com/cypress-io/cypress/issues/8086 @@ -84,12 +82,22 @@ describe('hooks', { passCount: 1, }) - cy.contains('test wrapper').parents('.collapsible').first().should(($suite) => { + cy.contains('test wrapper > nested suite 1').parents('.collapsible').first().should(($suite) => { expect($suite).not.to.contain('test 1') expect($suite).to.contain('nested suite 1') expect($suite).to.contain('test 2') expect($suite).not.to.contain('nested suite 2') expect($suite).not.to.contain('test 3') + expect($suite).not.to.contain('nested suite 3') + expect($suite).not.to.contain('test 4') + }) + + cy.contains('test wrapper > nested suite 3').parents('.collapsible').first().should(($suite) => { + expect($suite).not.to.contain('test 1') + expect($suite).not.to.contain('nested suite 1') + expect($suite).not.to.contain('test 2') + expect($suite).not.to.contain('nested suite 2') + expect($suite).not.to.contain('test 3') expect($suite).to.contain('nested suite 3') expect($suite).to.contain('test 4') }) diff --git a/packages/app/cypress/e2e/runner/runner.ui.cy.ts b/packages/app/cypress/e2e/runner/runner.ui.cy.ts index be616e889512..b7aa08b9882f 100644 --- a/packages/app/cypress/e2e/runner/runner.ui.cy.ts +++ b/packages/app/cypress/e2e/runner/runner.ui.cy.ts @@ -171,8 +171,9 @@ describe('src/cypress/runner', () => { o.sinon.stub(ctx.actions.file, 'openFile') }) - cy.contains('a', 'simple-cy-assert.runner') - .click() + cy.get('.open-in-ide-button').should('have.css', 'opacity', '0') + cy.get('.runnable-header-file-name').realHover() + cy.get('.open-in-ide-button').first().should('have.css', 'opacity', '1').click() cy.withCtx((ctx, o) => { expect(ctx.actions.file.openFile).to.have.been.calledWith(o.sinon.match(new RegExp(`simple-cy-assert\.runner\.cy\.js$`)), 1, 1) diff --git a/packages/app/cypress/e2e/runner/sessions.ui.cy.ts b/packages/app/cypress/e2e/runner/sessions.ui.cy.ts index d4a771e199cc..8aafd2d1f542 100644 --- a/packages/app/cypress/e2e/runner/sessions.ui.cy.ts +++ b/packages/app/cypress/e2e/runner/sessions.ui.cy.ts @@ -386,7 +386,7 @@ describe('runner/cypress sessions.ui.spec', { .within(() => { cy.contains('.command-wrapper', 'Create new session') .should('have.class', 'command-state-failed') - .find('.failed-indicator') + .find('[data-cy="failed-icon-indicator"]') .should('exist') }) }) @@ -465,7 +465,7 @@ describe('runner/cypress sessions.ui.spec', { cy.contains('.command-wrapper', 'Validate session').as('validateSessionGroup') .should('have.class', 'command-state-failed') - .find('.failed-indicator') + .find('[data-cy="failed-icon-indicator"]') .should('exist') }) }) @@ -528,7 +528,7 @@ describe('runner/cypress sessions.ui.spec', { cy.contains('.command-wrapper', 'Validate session').as('validateSessionGroup') .should('have.class', 'command-state-failed') - .find('.failed-indicator') + .find('[data-cy="failed-icon-indicator"]') .should('exist') const restoredMessagePostfix = 'This error occurred while validating the restored session. Because validation failed, we will try to recreate the session.' @@ -545,7 +545,7 @@ describe('runner/cypress sessions.ui.spec', { cy.contains('.command-wrapper', 'Recreate session') .should('have.class', successfullyRecreatedSession ? 'command-state-passed' : 'command-state-failed') - .find('.failed-indicator') + .find('[data-cy="failed-icon-indicator"]') .should(successfullyRecreatedSession ? 'not.exist' : 'exist', 'is-open') }) }) diff --git a/packages/app/cypress/e2e/runner/support/spec-loader.ts b/packages/app/cypress/e2e/runner/support/spec-loader.ts index 651a6a611153..94182aff830e 100644 --- a/packages/app/cypress/e2e/runner/support/spec-loader.ts +++ b/packages/app/cypress/e2e/runner/support/spec-loader.ts @@ -5,9 +5,10 @@ export const shouldHaveTestResults = ({ passCount, failCount, pendingCount }) => failCount = failCount || '--' cy.get('button.restart', { timeout: 30000 }).should('be.visible') // ensure tests are finished running - cy.findByLabelText('Stats', { timeout: 10000 }).within(() => { - cy.get('.passed .num', { timeout: 30000 }).should('have.text', `${passCount}`) - cy.get('.failed .num', { timeout: 30000 }).should('have.text', `${failCount}`) + + cy.get('.stats', { timeout: 10000 }).within(() => { + cy.get('.passed .num', { timeout: 40000 }).should('have.text', `${passCount}`) + cy.get('.failed .num', { timeout: 40000 }).should('have.text', `${failCount}`) if (pendingCount) { cy.get('.pending .num', { timeout: 20000 }).should('have.text', `${pendingCount}`) diff --git a/packages/app/cypress/e2e/runner/support/verify-failures.ts b/packages/app/cypress/e2e/runner/support/verify-failures.ts index 425a84d72b63..a730fafbcb3d 100644 --- a/packages/app/cypress/e2e/runner/support/verify-failures.ts +++ b/packages/app/cypress/e2e/runner/support/verify-failures.ts @@ -71,7 +71,7 @@ const verifyFailure = (options) => { cy.contains('.runnable-title', specTitle).closest('.runnable').as('Root') cy.get('@Root').within(() => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() const messageLines = [].concat(message) diff --git a/packages/app/cypress/e2e/sidebar_navigation.cy.ts b/packages/app/cypress/e2e/sidebar_navigation.cy.ts index d6e46fdfeeae..77f0bf6ddb29 100644 --- a/packages/app/cypress/e2e/sidebar_navigation.cy.ts +++ b/packages/app/cypress/e2e/sidebar_navigation.cy.ts @@ -56,7 +56,7 @@ describe('Sidebar Navigation', { viewportWidth: 1280 }, () => { cy.contains('fixture.js').click() - cy.get('.toggle-specs-text').click() + cy.get('.toggle-specs-button').click() cy.findByTestId('reporter-panel').invoke('outerWidth').then(($initialWidth) => { expect($initialWidth).eq(100) @@ -291,7 +291,7 @@ describe('Sidebar Navigation', { viewportWidth: 1280 }, () => { it.skip('resize nav and persist the state after refresh', () => { cy.contains('fixture.js').click() - cy.get('.toggle-specs-text').click() + cy.get('.toggle-specs-button').click() cy.withCtx((ctx, o) => { o.sinon.stub(ctx.actions.localSettings, 'setPreferences').resolves() diff --git a/packages/app/cypress/e2e/specs_list_component.cy.ts b/packages/app/cypress/e2e/specs_list_component.cy.ts index 6dffbbc57eb3..3ee75ebfb107 100644 --- a/packages/app/cypress/e2e/specs_list_component.cy.ts +++ b/packages/app/cypress/e2e/specs_list_component.cy.ts @@ -26,7 +26,7 @@ describe('App: Spec List (Component)', () => { it('highlights the currently running spec', () => { cy.contains('fails').click() - cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') + cy.get('[data-cy="runnable-header"]').should('be.visible') cy.get('body').type('f') cy.get('[data-selected-spec="true"]').should('contain', 'fails') diff --git a/packages/app/cypress/e2e/specs_list_e2e.cy.ts b/packages/app/cypress/e2e/specs_list_e2e.cy.ts index 58818aed91b7..1dcb1fc42120 100644 --- a/packages/app/cypress/e2e/specs_list_e2e.cy.ts +++ b/packages/app/cypress/e2e/specs_list_e2e.cy.ts @@ -119,7 +119,6 @@ describe('App: Spec List (E2E)', () => { cy.findAllByTestId('spec-item-link').should('have.attr', 'href') cy.findAllByTestId('spec-item-link').contains('dom-content.spec.js').click() - cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') cy.findByText('Your tests are loading...').should('not.be.visible') cy.get('[data-cy="runnable-header"]').should('be.visible') cy.get('body').type('f') @@ -133,10 +132,8 @@ describe('App: Spec List (E2E)', () => { cy.findAllByTestId('spec-item-link').contains('accounts_list.spec.js').click() // ensure the tests are loaded - cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') cy.findByText('Your tests are loading...').should('not.be.visible') - cy.contains('[aria-controls=reporter-inline-specs-list]', 'Specs') cy.get('[data-cy="runnable-header"]').should('be.visible') // open the inline spec list cy.get('body').type('f') @@ -369,7 +366,7 @@ describe('App: Spec List (E2E)', () => { // A bit of a hack, but our cy-in-cy test needs to wait for the reporter to fully render before expanding the "Search specs" menu. // Otherwise, the click happens before the event is registered, which causes the "Search Specs" menu to not expand. cy.get('[data-cy="runnable-header"]').should('be.visible') - cy.contains('button', 'Specs').click({ force: true }) + cy.findByTestId('toggle-specs-button').click({ force: true }) // wait until specs list is visible cy.findByTestId('specs-list-container').should('be.visible') diff --git a/packages/app/cypress/e2e/studio/helper.ts b/packages/app/cypress/e2e/studio/helper.ts index 6c66f9dbf475..34a9c03e5f71 100644 --- a/packages/app/cypress/e2e/studio/helper.ts +++ b/packages/app/cypress/e2e/studio/helper.ts @@ -30,9 +30,13 @@ export function launchStudio ({ specName = 'spec.cy.js', createNewTest = false, .closest('.runnable-wrapper').as('runnable-wrapper') .realHover() - cy.get('@runnable-wrapper') - .findByTestId('launch-studio') - .click() + if (createNewTest) { + cy.get('@runnable-wrapper').realHover().findByTestId('create-new-test-button').click() + } else { + cy.get('@runnable-wrapper') + .findByTestId('launch-studio') + .click() + } // Studio re-executes spec before waiting for commands - wait for the spec to finish executing. cy.waitForSpecToFinish() diff --git a/packages/driver/cypress/support/utils.ts b/packages/driver/cypress/support/utils.ts index 486f2dd40e4e..08ef205d357b 100644 --- a/packages/driver/cypress/support/utils.ts +++ b/packages/driver/cypress/support/utils.ts @@ -9,7 +9,7 @@ export const getCommandLogWithText = (command, type?) => { cy.$$('.runnable-active .collapsible:not(.is-open) .collapsible-header', top?.document).click() return cy - .$$(`.runnable-active .command-${type}:contains(${command})`, top?.document) + .$$(`.test.runnable-active .command-${type}:contains(${command})`, top?.document) .closest('.command') } diff --git a/packages/frontend-shared/package.json b/packages/frontend-shared/package.json index a432ec5ae80f..75fba94bc8c8 100644 --- a/packages/frontend-shared/package.json +++ b/packages/frontend-shared/package.json @@ -88,7 +88,6 @@ "just-my-luck": "3.0.0", "lodash": "4.17.21", "markdown-it": "13.0.1", - "modern-normalize": "1.1.0", "nock": "13.2.9", "p-defer": "^3.0.0", "patch-package": "8.0.0", diff --git a/packages/frontend-shared/src/assets/icons/status-processing_x12.svg b/packages/frontend-shared/src/assets/icons/status-processing_x12.svg deleted file mode 100644 index 2b33cc2eb4fe..000000000000 --- a/packages/frontend-shared/src/assets/icons/status-processing_x12.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/packages/frontend-shared/src/styles/normalize.scss b/packages/frontend-shared/src/styles/normalize.scss deleted file mode 100644 index 16ca33e1e3e3..000000000000 --- a/packages/frontend-shared/src/styles/normalize.scss +++ /dev/null @@ -1,825 +0,0 @@ -/** - * Tailwind's Preflight Style Reset - * https://tailwindcss.com/docs/preflight - * - * Why is this here? - * 1. Tailwind doesn't publish their style reset (which is - * derived from modern-normalize). - * 2. TailwindCSS's version of this doesn't work with third-party - * DOM elements that it can't extract. - */ - - /*! tailwindcss v2.2.16 | MIT License | https://tailwindcss.com */ -/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ -@use 'modern-normalize/modern-normalize.css'; - -/* -Document -======== -*/ - -/** -Use a better box model (opinionated). -*/ - -*, -::before, -::after { - box-sizing: border-box; -} - -/** -Use a more readable tab size (opinionated). -*/ - -html { - -moz-tab-size: 4; - tab-size: 4; -} - -/** -1. Correct the line height in all browsers. -2. Prevent adjustments of font size after orientation changes in iOS. -*/ - -html { - line-height: 1.15; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/* -Sections -======== -*/ - -/** -Remove the margin in all browsers. -*/ - -body { - margin: 0; -} - -/** -Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) -*/ - -body { - font-family: - system-ui, - -apple-system, /* Firefox supports this but not yet `system-ui` */ - 'Segoe UI', - Roboto, - Helvetica, - Arial, - sans-serif, - 'Apple Color Emoji', - 'Segoe UI Emoji'; -} - -/* -Grouping content -================ -*/ - -/** -1. Add the correct height in Firefox. -2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) -*/ - -hr { - height: 0; /* 1 */ - color: inherit; /* 2 */ -} - -/* -Text-level semantics -==================== -*/ - -/** -Add the correct text decoration in Chrome, Edge, and Safari. -*/ - -abbr[title] { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; -} - -/** -Add the correct font weight in Edge and Safari. -*/ - -b, -strong { - font-weight: bolder; -} - -/** -1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) -2. Correct the odd 'em' font sizing in all browsers. -*/ - -code, -kbd, -samp, -pre { - font-family: - ui-monospace, - SFMono-Regular, - Consolas, - 'Liberation Mono', - Menlo, - monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** -Add the correct font size in all browsers. -*/ - -small { - font-size: 80%; -} - -/** -Prevent 'sub' and 'sup' elements from affecting the line height in all browsers. -*/ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* -Tabular data -============ -*/ - -/** -1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) -2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) -*/ - -table { - text-indent: 0; /* 1 */ - border-color: inherit; /* 2 */ -} - -/* -Forms -===== -*/ - -/** -1. Change the font styles in all browsers. -2. Remove the margin in Firefox and Safari. -*/ - -button, -input, -optgroup, -select, -textarea { - font-family: inherit; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** -Remove the inheritance of text transform in Edge and Firefox. -1. Remove the inheritance of text transform in Firefox. -*/ - -button, -select { /* 1 */ - text-transform: none; -} - -/** -Correct the inability to style clickable types in iOS and Safari. -*/ - -button, -[type='button'], -[type='reset'], -[type='submit'] { - -webkit-appearance: button; -} - -/** -Remove the inner border and padding in Firefox. -*/ - -::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** -Restore the focus styles unset by the previous rule. -*/ - -:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** -Remove the additional ':invalid' styles in Firefox. -See: https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737 -*/ - -:-moz-ui-invalid { - box-shadow: none; -} - -/** -Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers. -*/ - -legend { - padding: 0; -} - -/** -Add the correct vertical alignment in Chrome and Firefox. -*/ - -progress { - vertical-align: baseline; -} - -/** -Correct the cursor style of increment and decrement buttons in Safari. -*/ - -::-webkit-inner-spin-button, -::-webkit-outer-spin-button { - height: auto; -} - -/** -1. Correct the odd appearance in Chrome and Safari. -2. Correct the outline style in Safari. -*/ - -[type='search'] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** -Remove the inner padding in Chrome and Safari on macOS. -*/ - -::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** -1. Correct the inability to style clickable types in iOS and Safari. -2. Change font properties to 'inherit' in Safari. -*/ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* -Interactive -=========== -*/ - -/* -Add the correct display in Chrome and Safari. -*/ - -summary { - display: list-item; -} - -/** - * Manually forked from SUIT CSS Base: https://github.com/suitcss/base - * A thin layer on top of normalize.css that provides a starting point more - * suitable for web applications. - */ - -/** - * Removes the default spacing and border for appropriate elements. - */ - -blockquote, -dl, -dd, -h1, -h2, -h3, -h4, -h5, -h6, -hr, -figure, -p, -pre { - margin: 0; -} - -button { - background-color: transparent; - background-image: none; -} - -fieldset { - margin: 0; - padding: 0; -} - -ol, -ul { - list-style: none; - margin: 0; - padding: 0; -} - -/** - * Tailwind custom reset styles - */ - -/** - * 1. Use the user's configured `sans` font-family (with Tailwind's default - * sans-serif font stack as a fallback) as a sane default. - * 2. Use Tailwind's default "normal" line-height so the user isn't forced - * to override it to ensure consistency even when using the default theme. - */ - -html { - font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 1 */ - line-height: 1.5; /* 2 */ -} - -/** - * Inherit font-family and line-height from `html` so users can set them as - * a class directly on the `html` element. - */ - -body { - font-family: inherit; - line-height: inherit; -} - -/** - * 1. Prevent padding and border from affecting element width. - * - * We used to set this in the html element and inherit from - * the parent element for everything else. This caused issues - * in shadow-dom-enhanced elements like
where the content - * is wrapped by a div with box-sizing set to `content-box`. - * - * https://github.com/mozdevs/cssremedy/issues/4 - * - * - * 2. Allow adding a border to an element by just adding a border-width. - * - * By default, the way the browser specifies that an element should have no - * border is by setting it's border-style to `none` in the user-agent - * stylesheet. - * - * In order to easily add borders to elements by just setting the `border-width` - * property, we change the default border-style for all elements to `solid`, and - * use border-width to hide them instead. This way our `border` utilities only - * need to set the `border-width` property instead of the entire `border` - * shorthand, making our border utilities much more straightforward to compose. - * - * https://github.com/tailwindcss/tailwindcss/pull/116 - */ - -*, -::before, -::after { - box-sizing: border-box; /* 1 */ - border-width: 0; /* 2 */ - border-style: solid; /* 2 */ - border-color: currentColor; /* 2 */ -} - -/* - * Ensure horizontal rules are visible by default - */ - -hr { - border-top-width: 1px; -} - -/** - * Undo the `border-style: none` reset that Normalize applies to images so that - * our `border-{width}` utilities have the expected effect. - * - * The Normalize reset is unnecessary for us since we default the border-width - * to 0 on all elements. - * - * https://github.com/tailwindcss/tailwindcss/issues/362 - */ - -img { - border-style: solid; -} - -textarea { - resize: vertical; -} - -input::placeholder, -textarea::placeholder { - opacity: 1; - color: #9ca3af; -} - -button, -[role="button"] { - cursor: pointer; -} - -/** - * Override legacy focus reset from Normalize with modern Firefox focus styles. - * - * This is actually an improvement over the new defaults in Firefox in our testing, - * as it triggers the better focus styles even for links, which still use a dotted - * outline in Firefox by default. - */ - -:-moz-focusring { - outline: auto; -} - -table { - border-collapse: collapse; -} - -h1, -h2, -h3, -h4, -h5, -h6 { - font-size: inherit; - font-weight: inherit; -} - -/** - * Reset links to optimize for opt-in styling instead of - * opt-out. - */ - -a { - color: inherit; - text-decoration: inherit; -} - -/** - * Reset form element properties that are easy to forget to - * style explicitly so you don't inadvertently introduce - * styles that deviate from your design system. These styles - * supplement a partial reset that is already applied by - * normalize.css. - */ - -button, -input, -optgroup, -select, -textarea { - padding: 0; - line-height: inherit; - color: inherit; -} - -/** - * Use the configured 'mono' font family for elements that - * are expected to be rendered with a monospace font, falling - * back to the system monospace stack if there is no configured - * 'mono' font family. - */ - -pre, -code, -kbd, -samp { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; -} - -/** - * 1. Make replaced elements `display: block` by default as that's - * the behavior you want almost all of the time. Inspired by - * CSS Remedy, with `svg` added as well. - * - * https://github.com/mozdevs/cssremedy/issues/14 - * - * 2. Add `vertical-align: middle` to align replaced elements more - * sensibly by default when overriding `display` by adding a - * utility like `inline`. - * - * This can trigger a poorly considered linting error in some - * tools but is included by design. - * - * https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210 - */ - -img, -svg, -video, -canvas, -audio, -iframe, -embed, -object { - display: block; /* 1 */ - vertical-align: middle; /* 2 */ -} - -/** - * Constrain images and videos to the parent width and preserve - * their intrinsic aspect ratio. - * - * https://github.com/mozdevs/cssremedy/issues/14 - */ - -img, -video { - max-width: 100%; - height: auto; -} - -/** - * Ensure the default browser behavior of the `hidden` attribute. - */ - -[hidden] { - display: none; -} - -*, ::before, ::after { - --tw-border-opacity: 1; - border-color: rgba(229, 231, 235, var(--tw-border-opacity)); -} - - -/* -* Global Resets -* ------------------------- -*/ - -// Input Resets -// Taken from Tailwind CSS Forms -- a plugin for tailwind. -// We could probably add these into TailwindCSS directly -// https://github.com/tailwindlabs/tailwindcss-forms - -[multiple], -[type=date], -[type=datetime-local], -[type=email], -[type=month], -[type=number], -[type=password], -[type=search], -[type=tel], -[type=text], -[type=time], -[type=url], -[type=week], -select, -textarea { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - border-radius: 0; - padding-top: 0.35rem; - padding-bottom: 0.35rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - font-size: 1rem; - line-height: 1.25rem; -} - -[multiple]:focus, -[type=date]:focus, -[type=datetime-local]:focus, -[type=email]:focus, -[type=month]:focus, -[type=number]:focus, -[type=password]:focus, -[type=search]:focus, -[type=tel]:focus, -[type=text]:focus, -[type=time]:focus, -[type=url]:focus, -[type=week]:focus, -select:focus, -textarea:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty, ); - /*!*/ - /*!*/ - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - border-color: #2563eb; -} - -input::-moz-placeholder, -textarea::-moz-placeholder { - color: #6b7280; - opacity: 1; -} - -input:-ms-input-placeholder, -textarea:-ms-input-placeholder { - color: #6b7280; - opacity: 1; -} - -input::placeholder, -textarea::placeholder { - color: #6b7280; - opacity: 1; -} - -::-webkit-datetime-edit-fields-wrapper { - padding: 0; -} - -::-webkit-date-and-time-value { - min-height: 1.5em; -} - -select { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); - background-position: right .5rem center; - background-repeat: no-repeat; - background-size: 1.5em 1.5em; - padding-right: 2.5rem; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; -} - -[multiple] { - background-image: initial; - background-position: initial; - background-repeat: unset; - background-size: initial; - padding-right: .75rem; - -webkit-print-color-adjust: unset; - print-color-adjust: unset; -} - -[type=checkbox], -[type=radio] { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - padding: 0; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - display: inline-block; - vertical-align: middle; - background-origin: border-box; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - flex-shrink: 0; - height: 1rem; - width: 1rem; - color: #2563eb; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; -} - -[type=checkbox] { - border-radius: 0; -} - -[type=radio] { - border-radius: 100%; -} - -[type=checkbox]:focus, -[type=radio]:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty); - --tw-ring-offset-width: 2px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); -} - -[type=checkbox]:checked, -[type=radio]:checked { - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -[type=checkbox]:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); -} - -[type=radio]:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); -} - -[type=checkbox]:checked:focus, -[type=checkbox]:checked:hover, -[type=radio]:checked:focus, -[type=radio]:checked:hover { - border-color: transparent; - background-color: currentColor; -} - -[type=checkbox]:indeterminate { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -[type=checkbox]:indeterminate:focus, -[type=checkbox]:indeterminate:hover { - border-color: transparent; - background-color: currentColor; -} - -[type=file] { - background: unset; - border-color: inherit; - border-width: 0; - border-radius: 0; - padding: 0; - font-size: unset; - line-height: inherit; -} - -[type=file]:focus { - outline: 1px auto -webkit-focus-ring-color; -} - - -input[type="search"].dark { - background: #222; - color: #fff; -} - -input[type="search"].light { - background: #fff; - color: #222; -} -input[type="search"].suffix::-webkit-search-cancel-button { - @apply pr-[3.25em]; -} - -input[type="search"]::-webkit-search-cancel-button { - -webkit-appearance: none; - margin: 1em; - height: 1em; - width: 1em; - border-radius: 50em; - background: url(https://pro.fontawesome.com/releases/v5.10.0/svgs/solid/times-circle.svg) - no-repeat 50% 50%; - background-size: contain; - opacity: 0; - pointer-events: none; -} - -input[type="search"]:focus::-webkit-search-cancel-button { - opacity: 0.3; - pointer-events: all; -} - -input[type="search"].dark::-webkit-search-cancel-button { - filter: invert(1); -} diff --git a/packages/frontend-shared/src/styles/shared.scss b/packages/frontend-shared/src/styles/shared.scss index 708128d3ad66..c60c88b0946b 100644 --- a/packages/frontend-shared/src/styles/shared.scss +++ b/packages/frontend-shared/src/styles/shared.scss @@ -1,4 +1,3 @@ -@use './normalize.scss'; /* Define the "system" font family */ @font-face { @@ -61,257 +60,4 @@ body { ::selection { @apply bg-gray-200 bg-opacity-30; -} - -/** - * Global Resets - * ------------------------- - */ - -// Input Resets -// Taken from Tailwind CSS Forms -- a plugin for tailwind. -// We could probably add these into TailwindCSS directly -// https://github.com/tailwindlabs/tailwindcss-forms -// [type="search"]::-webkit-search-cancel-button, -// [type="search"]::-webkit-search-decoration { -// -webkit-appearance: none !important; -// appearance: none !important; -// } - -[multiple], -[type=date], -[type=datetime-local], -[type=email], -[type=month], -[type=number], -[type=password], -[type=search], -[type=tel], -[type=text], -[type=time], -[type=url], -[type=week], -select, -textarea { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; - border-radius: 0; - padding-top: 0.35rem; - padding-bottom: 0.35rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - font-size: 1rem; - line-height: 1.25rem; -} - -[multiple]:focus, -[type=date]:focus, -[type=datetime-local]:focus, -[type=email]:focus, -[type=month]:focus, -[type=number]:focus, -[type=password]:focus, -[type=search]:focus, -[type=tel]:focus, -[type=text]:focus, -[type=time]:focus, -[type=url]:focus, -[type=week]:focus, -select:focus, -textarea:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty, ); - /*!*/ - /*!*/ - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); - border-color: #2563eb; -} - -input::-moz-placeholder, -textarea::-moz-placeholder { - color: #6b7280; - opacity: 1; -} - -input:-ms-input-placeholder, -textarea:-ms-input-placeholder { - color: #6b7280; - opacity: 1; -} - -input::placeholder, -textarea::placeholder { - color: #6b7280; - opacity: 1; -} - -::-webkit-datetime-edit-fields-wrapper { - padding: 0; -} - -::-webkit-date-and-time-value { - min-height: 1.5em; -} - -select { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); - background-position: right .5rem center; - background-repeat: no-repeat; - background-size: 1.5em 1.5em; - padding-right: 2.5rem; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; -} - -[multiple] { - background-image: initial; - background-position: initial; - background-repeat: unset; - background-size: initial; - padding-right: .75rem; - -webkit-print-color-adjust: unset; - print-color-adjust: unset; -} - -[type=checkbox], -[type=radio] { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - padding: 0; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - display: inline-block; - vertical-align: middle; - background-origin: border-box; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - flex-shrink: 0; - height: 1rem; - width: 1rem; - color: #2563eb; - background-color: #fff; - border-color: #6b7280; - border-width: 1px; -} - -[type=checkbox] { - border-radius: 0; -} - -[type=radio] { - border-radius: 100%; -} - -[type=checkbox]:focus, -[type=radio]:focus { - outline: 2px solid transparent; - outline-offset: 2px; - --tw-ring-inset: var(--tw-empty); - --tw-ring-offset-width: 2px; - --tw-ring-offset-color: #fff; - --tw-ring-color: #2563eb; - --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); -} - -[type=checkbox]:checked, -[type=radio]:checked { - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -[type=checkbox]:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); -} - -[type=radio]:checked { - background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); -} - -[type=checkbox]:checked:focus, -[type=checkbox]:checked:hover, -[type=radio]:checked:focus, -[type=radio]:checked:hover { - border-color: transparent; - background-color: currentColor; -} - -[type=checkbox]:indeterminate { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); - border-color: transparent; - background-color: currentColor; - background-size: 100% 100%; - background-position: center; - background-repeat: no-repeat; -} - -[type=checkbox]:indeterminate:focus, -[type=checkbox]:indeterminate:hover { - border-color: transparent; - background-color: currentColor; -} - -[type=file] { - background: unset; - border-color: inherit; - border-width: 0; - border-radius: 0; - padding: 0; - font-size: unset; - line-height: inherit; -} - -[type=file]:focus { - outline: 1px auto -webkit-focus-ring-color; -} - - -input[type="search"].dark { - background: #222; - color: #fff; -} - -input[type="search"].light { - background: #fff; - color: #222; -} -input[type="search"].suffix::-webkit-search-cancel-button { - @apply pr-[3.25em]; -} - -input[type="search"]::-webkit-search-cancel-button { - -webkit-appearance: none; - margin: 1em; - height: 1em; - width: 1em; - border-radius: 50em; - background: url(https://pro.fontawesome.com/releases/v5.10.0/svgs/solid/times-circle.svg) - no-repeat 50% 50%; - background-size: contain; - opacity: 0; - pointer-events: none; -} - -input[type="search"]:focus::-webkit-search-cancel-button { - opacity: 0.3; - pointer-events: all; -} - -input[type="search"].dark::-webkit-search-cancel-button { - filter: invert(1); -} +} \ No newline at end of file diff --git a/packages/reporter/README.md b/packages/reporter/README.md index afdcc2b5758a..0b4b294f4abe 100644 --- a/packages/reporter/README.md +++ b/packages/reporter/README.md @@ -16,20 +16,6 @@ The reporter shows the running results of the tests. It includes the following: - commands and assertions with detailed information - any failures/errors -## Building - -### For development - -```bash -yarn workspace @packages/reporter build -``` - -### For production - -```bash -yarn workspace @packages/reporter build-prod -``` - ## Developing To see the reporter render, see [Developing the driver](../driver/README.md#Developing). diff --git a/packages/reporter/cypress/e2e/commands.cy.ts b/packages/reporter/cypress/e2e/commands.cy.ts index ce0267afb0ad..86f5ef0544d0 100644 --- a/packages/reporter/cypress/e2e/commands.cy.ts +++ b/packages/reporter/cypress/e2e/commands.cy.ts @@ -898,7 +898,10 @@ describe('commands', { viewportHeight: 1000 }, () => { }) it('shows a tooltip', () => { - cy.get('.command-name-within').click('top') + cy.get('.command-name-within').within(() => { + cy.contains('within').click() + }) + cy.get('.cy-tooltip').should('have.text', 'Printed output to your console') }) @@ -911,21 +914,32 @@ describe('commands', { viewportHeight: 1000 }, () => { it('prints to console', () => { cy.spy(runner, 'emit') - cy.get('.command-name-within').click('top') + cy.get('.command-name-within').within(() => { + cy.contains('within').click() + }) cy.wrap(runner.emit).should('be.calledWith', 'runner:console:log', 'r3', fakeIdForTest) }) it('shows the snapshot', () => { cy.spy(runner, 'emit') - cy.get('.command-name-within').click('top') + cy.get('.command-name-within').within(() => { + cy.contains('within').click() + }) + cy.wrap(runner.emit).should('be.calledWith', 'runner:show:snapshot', 'r3', fakeIdForTest) }) it('unpins after clicking again, does not re-print to the console', () => { cy.spy(runner, 'emit') - cy.get('.command-name-within').click('top') - cy.get('.command-name-within').click('top') + cy.get('.command-name-within').within(() => { + cy.contains('within').click() + }) + + cy.get('.command-name-within').within(() => { + cy.contains('within').click() + }) + // @ts-ignore cy.wrap(runner.emit.withArgs('runner:console:log')).should('be.calledOnce') }) diff --git a/packages/reporter/cypress/e2e/header.cy.ts b/packages/reporter/cypress/e2e/header.cy.ts index 4e4c8cc1b2d5..3bb1e9be5259 100755 --- a/packages/reporter/cypress/e2e/header.cy.ts +++ b/packages/reporter/cypress/e2e/header.cy.ts @@ -57,7 +57,7 @@ describe('header', () => { }) it('shows \'Tests\' when >= 398px wide', () => { - cy.get('.toggle-specs-wrapper span').should('be.visible') + cy.get('[data-cy=toggle-specs-button]').should('be.visible') }) }) diff --git a/packages/reporter/cypress/e2e/meta_&%.cy.ts b/packages/reporter/cypress/e2e/meta_&%.cy.ts index 9afdea03c851..0f424b0cbc2c 100644 --- a/packages/reporter/cypress/e2e/meta_&%.cy.ts +++ b/packages/reporter/cypress/e2e/meta_&%.cy.ts @@ -3,7 +3,7 @@ describe('special characters', () => { it('displays file name with decoded special characters', () => { cy.wrap(Cypress.$(window.top.document.body)) - .find('.reporter .runnable-header a') - .should('have.text', 'meta_&%.cy.ts') + .find('.reporter .runnable-header') + .contains('meta_&%.cy.ts') }) }) diff --git a/packages/reporter/cypress/e2e/runnables.cy.ts b/packages/reporter/cypress/e2e/runnables.cy.ts index eb108faa239d..966b8cd653d1 100644 --- a/packages/reporter/cypress/e2e/runnables.cy.ts +++ b/packages/reporter/cypress/e2e/runnables.cy.ts @@ -137,8 +137,8 @@ describe('runnables', () => { it('does not display time if no time taken', () => { start() - cy.get('.runnable-header span:first').should('have.text', 'foo.js') - cy.get('.runnable-header span:last').should('not.have.text', '--') + cy.get('.runnable-header .runnable-header-file-name').contains('foo.js') + cy.get('.runnable-header .duration').should('not.exist') }) describe('when there are no tests', () => { @@ -204,11 +204,12 @@ describe('runnables', () => { }) it('contains name of spec and emits when clicked', () => { - const selector = '.runnable-header a' + const selector = '.runnable-header-file-name' cy.stub(runner, 'emit').callThrough() - cy.get(selector).as('spec-title').contains('foo.js') + cy.get(selector).as('spec-title').contains('foo.js').realHover() + cy.get('.open-in-ide-button').click() cy.get(selector).click().then(() => { expect(runner.emit).to.be.calledWith('open:file:unified') }) diff --git a/packages/reporter/cypress/e2e/shortcuts.cy.ts b/packages/reporter/cypress/e2e/shortcuts.cy.ts index 4ada861cb4ff..c31c1bb7c39c 100755 --- a/packages/reporter/cypress/e2e/shortcuts.cy.ts +++ b/packages/reporter/cypress/e2e/shortcuts.cy.ts @@ -82,17 +82,17 @@ describe('shortcuts', function () { cy.get('body').then(() => { expect(runner.emit).not.to.have.been.calledWith('save:state') - cy.contains('button', 'Specs').should('have.attr', 'aria-expanded', 'false') + cy.get('[data-cy=toggle-specs-button]').should('have.attr', 'aria-expanded', 'false') }) cy.get('body').type('f').then(() => { expect(runner.emit).to.have.been.calledWith('save:state') - cy.contains('button', 'Specs').should('have.attr', 'aria-expanded', 'true') + cy.get('[data-cy=toggle-specs-button]').should('have.attr', 'aria-expanded', 'true') }) cy.get('body').type('f').then(() => { expect(runner.emit).to.have.been.calledWith('save:state') - cy.contains('button', 'Specs').should('have.attr', 'aria-expanded', 'false') + cy.get('[data-cy=toggle-specs-button]').should('have.attr', 'aria-expanded', 'false') }) }) @@ -140,9 +140,9 @@ describe('shortcuts', function () { }) it('has shortcut in tooltips', () => { - cy.get('.toggle-specs-wrapper > button').trigger('mouseover') + cy.get('[data-cy=toggle-specs-button]').trigger('mouseover') cy.get('.cy-tooltip').should('have.text', 'Expand Specs List F') - cy.get('.toggle-specs-wrapper > button').trigger('mouseout') + cy.get('[data-cy=toggle-specs-button]').trigger('mouseout') cy.get('button.restart').trigger('mouseover') cy.get('.cy-tooltip').should('have.text', 'Run All Tests R') diff --git a/packages/reporter/cypress/e2e/spec_title.cy.ts b/packages/reporter/cypress/e2e/spec_title.cy.ts index 51f89915d69c..938c39044808 100644 --- a/packages/reporter/cypress/e2e/spec_title.cy.ts +++ b/packages/reporter/cypress/e2e/spec_title.cy.ts @@ -55,19 +55,24 @@ describe('spec title', () => { }) it('displays name without path', () => { - cy.get('.runnable-header').find('a').should('have.text', 'foo.js') + cy.get('.runnable-header-file-name').contains('foo.js') cy.percySnapshot() }) - it('displays tooltip on hover', () => { - cy.get('.runnable-header a').first().trigger('mouseover') - cy.get('.cy-tooltip').first().should('have.text', 'Open in IDE') + it('displays Open in IDE button on spec name hover', () => { + cy.get('.open-in-ide-button').should('have.css', 'opacity', '0') + + cy.get('.runnable-header-file-name').realHover() + cy.get('.open-in-ide-button').should('have.css', 'opacity', '1') + cy.get('.open-in-ide-button').contains('Open in IDE') + + cy.percySnapshot() }) itHandlesFileOpening({ getRunner: () => runner, - selector: '.runnable-header a', + selector: '.open-in-ide-button', file: { file: '/absolute/path/to/foo.js', line: 0, diff --git a/packages/reporter/cypress/e2e/suites.cy.ts b/packages/reporter/cypress/e2e/suites.cy.ts index ba91185ab3e7..61144a3eb10f 100644 --- a/packages/reporter/cypress/e2e/suites.cy.ts +++ b/packages/reporter/cypress/e2e/suites.cy.ts @@ -56,6 +56,10 @@ describe('suites', () => { .closest('.runnable') .should('have.class', 'runnable-failed') + cy.contains('suite 1 > nested suite 1') + .closest('.runnable') + .should('have.class', 'runnable-processing') + cy.contains('suite 2') .closest('.runnable') .should('have.class', 'runnable-passed') @@ -148,27 +152,17 @@ describe('suites', () => { cy.contains('nested suite 1') .closest('.runnable-wrapper') .realHover() - .find('.runnable-controls-studio') + .get('[data-cy="create-new-test-button"]') .should('be.visible') - .should('have.css', 'opacity', '0.5') - }) - - it('displays studio icon with no transparency and tooltip on hover', () => { - cy.contains('nested suite 1') - .closest('.collapsible-header') - .find('.runnable-controls-studio') - .realHover() - .should('be.visible') - .should('have.css', 'opacity', '1') - - cy.get('.cy-tooltip').contains('Add New Test') }) it('emits studio:init:suite with the suite id when clicked', () => { cy.stub(runner, 'emit') cy.contains('suite 1').parents('.collapsible-header') - .find('.runnable-controls-studio').click() + .realHover().within(() => { + cy.get('[data-cy="create-new-test-button"]').click() + }) cy.wrap(runner.emit).should('be.calledWith', 'studio:init:suite', 'r2') }) diff --git a/packages/reporter/cypress/e2e/test_errors.cy.ts b/packages/reporter/cypress/e2e/test_errors.cy.ts index a9f797e01e39..73770caf5d2d 100644 --- a/packages/reporter/cypress/e2e/test_errors.cy.ts +++ b/packages/reporter/cypress/e2e/test_errors.cy.ts @@ -70,7 +70,7 @@ describe('test errors', () => { it('does not expand or collapse stack trace when clicking', () => { cy.get('.runnable-err-print').click() cy.get('.runnable-err-stack-trace').should('not.exist') - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.get('.runnable-err-stack-trace').should('be.visible') cy.get('.runnable-err-print').click() cy.get('.runnable-err-stack-trace').should('be.visible') @@ -87,13 +87,13 @@ describe('test errors', () => { }) it('opens stack trace on click', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.get('.runnable-err-stack-trace').should('be.visible') cy.percySnapshot() }) it('pairs down stack line whitespace', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.get('.runnable-err-stack-trace').within(() => { cy.get('.err-stack-line') @@ -118,7 +118,7 @@ describe('test errors', () => { }) it('does not include message in stack trace', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.get('.runnable-err-stack-trace') .invoke('text') .should('not.include', 'Some Error') @@ -126,7 +126,7 @@ describe('test errors', () => { }) it('turns files into links', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.get('.runnable-err-stack-trace .runnable-err-file-path') .should('have.length', 3) @@ -141,34 +141,34 @@ describe('test errors', () => { }) it('does not turn cypress:// files into links', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.contains('cypress://').find('a').should('not.exist') }) it('does not turn cypress_runner.js files into links', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.contains('cypress_runner.js').find('a').should('not.exist') }) it('does not turn lines without absoluteFile into links', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.contains('.err-stack-line', 'http://localhost:1234/me/dev/my/app.js:8:11') .find('a').should('not.exist') }) it('does not turn anything after "From Node.js Internals" into links', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.contains('events.js').find('a').should('not.exist') cy.contains('node/internals.js').find('a').should('not.exist') }) it('does not collapse test when clicking', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.get('.command-wrapper').should('be.visible') }) it('displays tooltip on hover', () => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() cy.get('.runnable-err-stack-trace a').first().trigger('mouseover') cy.get('.cy-tooltip').first().should('have.text', 'Open in IDE') diff --git a/packages/reporter/cypress/e2e/tests.cy.ts b/packages/reporter/cypress/e2e/tests.cy.ts index b1ba923e33fa..7c97da55ae51 100644 --- a/packages/reporter/cypress/e2e/tests.cy.ts +++ b/packages/reporter/cypress/e2e/tests.cy.ts @@ -48,13 +48,25 @@ describe('tests', () => { }) it('includes the state as a class', () => { - cy.contains('suite 1') - .closest('.runnable') - .should('have.class', 'runnable-failed') + cy.get('.suite').first().within((el) => { + cy.wrap(el).contains('suite 1') + cy.get('.test').eq(0).should('have.class', 'runnable-passed') + cy.get('.test').eq(1).should('have.class', 'runnable-failed') + }) - cy.contains('suite 2') - .closest('.runnable') - .should('have.class', 'runnable-passed') + cy.get('.suite').eq(1).within((el) => { + cy.wrap(el).contains('suite 1 > nested suite 1') + cy.get('.test').eq(0).should('have.class', 'runnable-pending') + cy.get('.test').eq(1).should('have.class', 'runnable-active') + }) + + cy.get('.suite').eq(2).within((el) => { + cy.wrap(el).contains('suite 2') + cy.get('.test').eq(0).should('have.class', 'runnable-passed') + cy.get('.test').eq(1).should('have.class', 'runnable-passed') + cy.get('.test').eq(2).should('have.class', 'runnable-passed') + .should('have.class', 'runnable-retried') + }) }) describe('expand and collapse', () => { @@ -284,7 +296,7 @@ describe('studio controls', () => { .realHover() .find('.runnable-controls-studio') .should('be.visible') - .should('have.css', 'opacity', '0.5') + .should('have.css', 'opacity', '1') }) it('displays studio icon with no transparency and tooltip on hover', { scrollBehavior: false }, () => { @@ -355,13 +367,9 @@ describe('studio controls', () => { it('is visible without save and copy button if test was skipped', () => { cy.contains('nested suite 1') .parents('.collapsible').first() - .contains('test 1').click() - .parents('.collapsible').first() - .find('.studio-controls').as('pendingControls') - .should('be.visible') - - cy.get('@pendingControls').find('.studio-save').should('not.be.visible') - cy.get('@pendingControls').find('.studio-copy').should('not.be.visible') + .contains('test 1').should('have.css', 'pointer-events', 'none') + .parents('.collapsible').first().scrollIntoView() + .find('.studio-controls').should('not.exist') }) it('is not visible while test is running', () => { diff --git a/packages/reporter/cypress/e2e/unit/suite_model.cy.ts b/packages/reporter/cypress/e2e/unit/suite_model.cy.ts index 8822c98e1dcd..09deb8246009 100644 --- a/packages/reporter/cypress/e2e/unit/suite_model.cy.ts +++ b/packages/reporter/cypress/e2e/unit/suite_model.cy.ts @@ -1,10 +1,10 @@ import Suite from '../../../src/runnables/suite-model' import TestModel from '../../../src/test/test-model' -const suiteWithChildren = (children: Array>) => { - const suite = new Suite({ id: '1', title: '', hooks: [] }, 0) +const suiteWithChildren = (children: Array>) => { + const suite = new Suite({ id: '1', title: '', hooks: [], suites: [], tests: [] }, 0) - suite.children = children as Array + suite.children = children.map((child) => ({ type: 'test', ...child })) as Array return suite } @@ -41,22 +41,25 @@ describe('Suite model', () => { expect(suite.state).to.equal('passed') }) - it('is processing when all children are active', () => { + // TODO: https://github.com/cypress-io/cypress-services/issues/11050 + it.skip('is active when all children are active', () => { const suite = suiteWithChildren([{ state: 'active' }, { state: 'active' }]) - expect(suite.state).to.equal('processing') + expect(suite.state).to.equal('active') }) - it('is processing when there are active tests with passing tests', () => { + // TODO: https://github.com/cypress-io/cypress-services/issues/11050 + it.skip('is active when there are active tests with passing tests', () => { const suite = suiteWithChildren([{ state: 'active' }, { state: 'passed' }]) - expect(suite.state).to.equal('processing') + expect(suite.state).to.equal('active') }) - it('is processing when there are active tests with pending tests', () => { + // TODO: https://github.com/cypress-io/cypress-services/issues/11050 + it.skip('is active when there are active tests with pending tests', () => { const suite = suiteWithChildren([{ state: 'active' }, { state: 'pending' }]) - expect(suite.state).to.equal('processing') + expect(suite.state).to.equal('active') }) it('is processing when all children are processing', () => { @@ -77,4 +80,52 @@ describe('Suite model', () => { expect(suite.state).to.equal('processing') }) }) + + describe('nested suites', () => { + it('is passed even when children suites are not', () => { + const suite = suiteWithChildren([{ state: 'passed', type: 'test' }, { state: 'active', type: 'suite' }, { state: 'failed', type: 'suite' }]) + + expect(suite.state).to.equal('passed') + expect(suite.children[0].state).to.equal('passed') + expect(suite.children[1].state).to.equal('active') + expect(suite.children[2].state).to.equal('failed') + }) + + it('is failed even when children suites are not', () => { + const suite = suiteWithChildren([{ state: 'failed' }, { state: 'passed', type: 'suite' }, { state: 'passed', type: 'suite' }]) + + expect(suite.state).to.equal('failed') + expect(suite.children[0].state).to.equal('failed') + expect(suite.children[1].state).to.equal('passed') + expect(suite.children[2].state).to.equal('passed') + }) + + // TODO: https://github.com/cypress-io/cypress-services/issues/11050 + it.skip('is active even when children suites are not', () => { + const suite = suiteWithChildren([{ state: 'active' }, { state: 'processing', type: 'suite' }, { state: 'passed', type: 'suite' }]) + + expect(suite.state).to.equal('active') + expect(suite.children[0].state).to.equal('active') + expect(suite.children[1].state).to.equal('processing') + expect(suite.children[2].state).to.equal('passed') + }) + + it('is processing even when children suites are not', () => { + const suite = suiteWithChildren([{ state: 'processing' }, { state: 'passed', type: 'suite' }, { state: 'pending', type: 'suite' }]) + + expect(suite.state).to.equal('processing') + expect(suite.children[0].state).to.equal('processing') + expect(suite.children[1].state).to.equal('passed') + expect(suite.children[2].state).to.equal('pending') + }) + + it('is pending even when children suites are not', () => { + const suite = suiteWithChildren([{ state: 'pending' }, { state: 'passed', type: 'suite' }, { state: 'failed', type: 'suite' }]) + + expect(suite.state).to.equal('pending') + expect(suite.children[0].state).to.equal('pending') + expect(suite.children[1].state).to.equal('passed') + expect(suite.children[2].state).to.equal('failed') + }) + }) }) diff --git a/packages/reporter/cypress/support/e2e.ts b/packages/reporter/cypress/support/e2e.ts index 2119e0d339c0..be8838067354 100644 --- a/packages/reporter/cypress/support/e2e.ts +++ b/packages/reporter/cypress/support/e2e.ts @@ -4,7 +4,7 @@ import { installCustomPercyCommand } from '@packages/frontend-shared/cypress/sup installCustomPercyCommand({ before () { - cy.get('.toggle-specs-text').should('be.visible') + cy.get('.toggle-specs-button').should('be.visible') }, elementOverrides: { '.command-progress': true, diff --git a/packages/reporter/cypress/support/utils.ts b/packages/reporter/cypress/support/utils.ts index 1ac9af04decf..f4bbd0e9f96f 100644 --- a/packages/reporter/cypress/support/utils.ts +++ b/packages/reporter/cypress/support/utils.ts @@ -22,7 +22,7 @@ export const itHandlesFileOpening = ({ getRunner, selector, file, stackTrace = f cy.stub(getRunner(), 'emit').callThrough() if (stackTrace) { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() } cy.get(selector).first().click().then(() => { diff --git a/packages/reporter/package.json b/packages/reporter/package.json index 5c62f5e04cbb..8f21b0ac373d 100644 --- a/packages/reporter/package.json +++ b/packages/reporter/package.json @@ -15,6 +15,10 @@ "watch": "yarn build-for-tests --watch --progress" }, "devDependencies": { + "@cypress-design/constants-button": "^1.9.0", + "@cypress-design/css": "1.2.0", + "@cypress-design/react-button": "^1.10.1", + "@cypress-design/react-icon": "^1.27.0", "@cypress/react-tooltip": "0.5.3", "@fontsource/mulish": "4.3.0", "@fontsource/open-sans": "4.3.0", @@ -24,6 +28,7 @@ "@packages/frontend-shared": "0.0.0-development", "@packages/types": "0.0.0-development", "@packages/web-config": "0.0.0-development", + "autoprefixer": "10.4.21", "classnames": "^2.5.1", "cross-env": "7.0.3", "css-element-queries": "1.2.3", @@ -33,11 +38,13 @@ "markdown-it": "^14.0.0", "mobx": "6.13.6", "mobx-react": "9.1.1", + "postcss": "8.5.6", "prismjs": "1.27.0", "prop-types": "15.7.2", "react": "18.3.1", "react-dom": "18.3.1", "sinon": "7.5.0", + "tailwindcss": "4.1.10", "webpack": "^5.88.2", "webpack-cli": "^5.1.4" }, diff --git a/packages/reporter/postcss.config.js b/packages/reporter/postcss.config.js new file mode 100644 index 000000000000..33ad091d26d8 --- /dev/null +++ b/packages/reporter/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/packages/reporter/src/OpenFileInIDEButton.tsx b/packages/reporter/src/OpenFileInIDEButton.tsx new file mode 100644 index 000000000000..8babe57898f6 --- /dev/null +++ b/packages/reporter/src/OpenFileInIDEButton.tsx @@ -0,0 +1,19 @@ +import Button from '@cypress-design/react-button' +import React from 'react' +import events from './lib/events' +import { IconWindowCodeEditor } from '@cypress-design/react-icon' +import { FileDetails } from '@packages/types' +import cx from 'classnames' + +interface Props { + fileDetails: FileDetails + className?: string +} + +export const OpenFileInIDEButton = ({ fileDetails, className }: Props) => { + return (<> + + + + ) +} diff --git a/packages/reporter/src/attempts/attempts.scss b/packages/reporter/src/attempts/attempts.scss index f88e9ee11762..8ccefd6cbb4b 100644 --- a/packages/reporter/src/attempts/attempts.scss +++ b/packages/reporter/src/attempts/attempts.scss @@ -1,40 +1,64 @@ .reporter { + // Mixin for dotted border line + @mixin dotted-border-line($position: "top") { + border-left: 1px dotted $gray-800; + content: ""; + left: 16px; + position: absolute; + height: 8px; + z-index: 1; + + @if $position == "top" { + top: 0; + } @else if $position == "bottom" { + bottom: 0; + } + } + .attempts { + border-right: 1px solid $gray-800; + border-bottom: 1px solid $gray-800; + border-radius: 0 0 4px 4px; + .attempt-item > .collapsible > .collapsible-header-wrapper { display: none; } - &.has-multiple-attempts .attempt-item > .collapsible > .collapsible-header-wrapper { - display: flex; + &.has-multiple-attempts .attempt-item { + > .collapsible > .collapsible-header-wrapper { + display: flex; + } + + &:not(:first-child) { + > .collapsible .attempt-name:before { + @include dotted-border-line("top"); + } + } + > .collapsible .attempt-name { + &:after { + @include dotted-border-line("bottom"); + } + } } } - .attempt-item { - margin-bottom: 7px; + .attempt-error-region { + margin: 0 8.5px; + } + .attempt-item { > .collapsible { position: relative; - margin-right: 16px; - .collapsible-header-inner { - outline: none; - } - &:before { - border-left: 1px solid $gray-900; - content: ''; - left: 9px; - position: absolute; - top: 22px; - height: 15px; + > .attempt-name > .collapsible-header { + .collapsible-header-inner { + outline: none; + display: flex; + align-items: center; + width: 100%; + padding: 0; + } } - - &.is-open:before { - display: none; - } - } - - &:last-child > .collapsible:before { - display: none; } > .is-open .open-close-indicator { @@ -47,76 +71,60 @@ } } - .open-close-indicator { - svg { - margin-right: 3px; + .open-close-indicator { + svg { + margin-right: 3px; - &.collapse-icon { - display: none; - } + &.collapse-icon { + display: none; + } - &.expand-icon { - display: block; + &.expand-icon { + display: block; + } } } - } - .attempt-content { - padding-left: 5px; - } -} + .attempt-content { + display: flex; + margin-bottom: 6px; - .attempt-state-failed { - .attempt-name:after { - color: $fail; + > div { + width: 100%; + } } - } - .attempt-state-passed { - .attempt-name:after { - color: $pass; + + &:not(:last-child) .attempt-name { + border-bottom: 1px solid $gray-900; } } - .attempt-name { display: flex; - justify-content: flex-end; position: relative; width: 100%; - &:before { - border-top: 1px solid $gray-900; - content: ''; - left: 15px; - position: absolute; - right: 0; - top: 13px; - } - - &:after { - color: $gray-600; - content: '•'; - left: 7px; - position: absolute; - top: 4px; + .collapsible-header { + width: 100%; } .attempt-tag { + display: inline-flex; align-items: center; - border: 1px solid $gray-900; - border-radius: 7px; - box-shadow: 0 1px 1px 0 rgba($white, 0.20); - display: flex; - font-size: 11px; - padding: 2px 5px; + font-size: 14px; + gap: 8px; + width: 100%; + padding: 8px; position: relative; - background-color: $black; user-select: none; cursor: pointer; &:hover { background-color: $gray-1100; } + .attempt-tag-text { + flex-grow: 1; + } } .collapsible-more { diff --git a/packages/reporter/src/attempts/attempts.tsx b/packages/reporter/src/attempts/attempts.tsx index 774b0ae6892e..5156e46d4849 100644 --- a/packages/reporter/src/attempts/attempts.tsx +++ b/packages/reporter/src/attempts/attempts.tsx @@ -24,14 +24,16 @@ const NoCommands = () => ( ) -const AttemptHeader = ({ index, state }: {index: number, state: TestState }) => ( +const AttemptHeader = ({ index, state }: { index: number, state: TestState }) => ( + + + Attempt {index + 1} + - Attempt {index + 1} - ) diff --git a/packages/reporter/src/collapsible/collapsible.scss b/packages/reporter/src/collapsible/collapsible.scss index 95ed565b95b5..b423ca0835cc 100644 --- a/packages/reporter/src/collapsible/collapsible.scss +++ b/packages/reporter/src/collapsible/collapsible.scss @@ -3,10 +3,6 @@ margin-right: 8px; transform: rotate(-90deg); transition: transform 150ms ease-out; - - .icon-dark { - stroke: $gray-800; - } } .is-open > .collapsible-header-wrapper > .collapsible-header > .collapsible-header-inner > .collapsible-indicator { diff --git a/packages/reporter/src/collapsible/collapsible.tsx b/packages/reporter/src/collapsible/collapsible.tsx index 7c2125a13207..287a5b768dac 100644 --- a/packages/reporter/src/collapsible/collapsible.tsx +++ b/packages/reporter/src/collapsible/collapsible.tsx @@ -1,13 +1,19 @@ import cs from 'classnames' import React, { CSSProperties, MouseEvent, ReactNode, RefObject, useCallback, useState } from 'react' import { onEnterOrSpace } from '../lib/util' -import ChevronIcon from '@packages/frontend-shared/src/assets/icons/chevron-down-small_x8.svg' +import DocumentBlankIcon from '@packages/frontend-shared/src/assets/icons/document-blank_x16.svg' +import { IconChevronDownSmall } from '@cypress-design/react-icon' + +export interface CollapsibleHeaderComponentProps { + isOpen: boolean +} interface CollapsibleProps { isOpen?: boolean headerClass?: string headerStyle?: CSSProperties header?: ReactNode + HeaderComponent?: React.FunctionComponent headerExtras?: ReactNode containerRef?: RefObject contentClass?: string @@ -16,7 +22,7 @@ interface CollapsibleProps { onOpenStateChangeRequested?: (isOpen: boolean) => void } -const Collapsible: React.FC = ({ isOpen: isOpenAsProp = false, header, headerClass = '', headerStyle = {}, headerExtras, contentClass = '', hideExpander = false, containerRef = null, onOpenStateChangeRequested, children }) => { +const Collapsible: React.FC = ({ isOpen: isOpenAsProp = false, header, headerClass = '', headerStyle = {}, headerExtras, contentClass = '', hideExpander = false, containerRef = null, onOpenStateChangeRequested, children, HeaderComponent }) => { const [isOpenState, setIsOpenState] = useState(isOpenAsProp) const toggleOpenState = useCallback((e?: MouseEvent) => { @@ -46,9 +52,10 @@ const Collapsible: React.FC = ({ isOpen: isOpenAsProp = false, style={headerStyle} tabIndex={-1} > - {!hideExpander && } + {!hideExpander && headerClass === 'hook-header' && } + {!hideExpander && headerClass !== 'hook-header' && } - {header} + {HeaderComponent ? : header} diff --git a/packages/reporter/src/commands/command.cy.tsx b/packages/reporter/src/commands/command.cy.tsx index 0958fc74be0b..dcc9771bf883 100644 --- a/packages/reporter/src/commands/command.cy.tsx +++ b/packages/reporter/src/commands/command.cy.tsx @@ -71,6 +71,10 @@ describe('commands', () => { state: 'failed', status: 'failed', }, + { + state: 'passed', + status: 'created', + }, ] it('session status in command', () => { @@ -103,6 +107,8 @@ describe('commands', () => { , ) + cy.get('.command-name-session').last().click() + cy.percySnapshot() }) }) diff --git a/packages/reporter/src/commands/command.tsx b/packages/reporter/src/commands/command.tsx index 83d813126a53..819102f60fce 100644 --- a/packages/reporter/src/commands/command.tsx +++ b/packages/reporter/src/commands/command.tsx @@ -55,13 +55,13 @@ export const formattedMessage = (message: string, name?: string) => { if (name === 'assert' && assertionArray) { const expectedActualArray = () => { - // get the expected and actual values of assertions + // get the expected and actual values of assertions const splitTrim = message.split(assertionRegex).filter(Boolean).map((s) => s.trim()) // replace outside double asterisks with strong tags return splitTrim.map((s) => { - // we want to escape HTML chars so that they display - // correctly in the command log:

-> <p> + // we want to escape HTML chars so that they display + // correctly in the command log:

-> <p> const HTMLEscapedString = mdOnlyHTML.renderInline(s) return HTMLEscapedString.replace(asterisksRegex, `$1`) @@ -192,8 +192,8 @@ const Interceptions: React.FC = observer(({ interceptions, wentToOr const interceptsTitle = ( - {wentToOrigin ? '' : <>This request did not go to origin because the response was stubbed.
} - This request matched: + {wentToOrigin ? '' : <>This request did not go to origin because the response was stubbed.
} + This request matched:

    {interceptions?.map(({ command, alias, type }, i) => (
  • @@ -331,7 +331,7 @@ const CommandDetails: React.FC = observer(({ model, groupId {model.event && model.type !== 'system' ? `(${displayName(model)})` : displayName(model)} - {!!groupId && model.type === 'system' && model.state === 'failed' && } + {!!groupId && model.type === 'system' && model.state === 'failed' && } {model.referencesAlias ? : @@ -509,13 +509,13 @@ const Command: React.FC = observer(({ model, aliasesWithDuplicates
    diff --git a/packages/reporter/src/commands/commands.scss b/packages/reporter/src/commands/commands.scss index 290869c55e0f..697267e79450 100644 --- a/packages/reporter/src/commands/commands.scss +++ b/packages/reporter/src/commands/commands.scss @@ -7,9 +7,11 @@ .reporter { // rendered within ../hooks/hooks.tsx .commands-container { - background-color: $reporter-section-background; min-width: $reporter-contents-min-width; padding: 0; + border: 1px solid $gray-900; + margin: 6px 8px 0 8px; + border-radius: 4px; &:empty { display: none; @@ -23,7 +25,7 @@ .command-is-studio { cursor: auto; - + &.command-type-parent .commands-controls .studio-command-remove { display: block; padding-left: 5px; @@ -62,12 +64,12 @@ } .command-wrapper { - border-left: 2px solid $reporter-section-background; - background-color: $reporter-section-background; + border-left: 2px solid transparent; + border-radius: 4px; color: $gray-500; display: flex; min-height: 28px; - padding-right: 2px; + align-items: center; &.command-is-interactive:hover { background-color: $gray-900; @@ -95,12 +97,12 @@ .alias-container { margin-left: 0; white-space: nowrap; - + > * { display: inline-block; margin-left: 2px; } - + > *:first-child { margin-left: 0; } @@ -109,14 +111,13 @@ .command-number-column { @include gutter-alignment; - - color: #5a5f7a; + color: $gray-500; } // when no children, add padding to act as the .command-expander-column's width // to prevent adding another element to the page .command-number-column + span.command-pin-target { - margin-left: $gutter-margin; + margin-left: 24px; } .command-pin-target.command-group { @@ -131,20 +132,19 @@ } .command-group-no-children { - padding-left: 15px; + padding-left: 15px; } .command-wrapper-text-group { padding-left: 15px; width: 100%; - } .command-wrapper-text-group-parent { padding-left: 5px; } - .nested-group-expander { + .nested-group-expander { .command-expander { position: relative; margin-left: -16px !important; // Adjust this value to center the caret on the border @@ -181,7 +181,7 @@ } .fa-circle.command-message-indicator-bad { - color: $red-500 + color: $red-500; } .fa-circle.command-message-indicator-pending { @@ -207,12 +207,13 @@ &.command-is-interactive:hover { border-left: 2px solid $gray-900; + border-radius: 0; } &:not(.command-is-event) .command-number { color: $gray-700; } - + &:not(.command-is-event, .command-type-system) .command-method { color: $gray-200; } @@ -222,8 +223,10 @@ } } + // pending is running in this case .command-state-pending { border-left: 2px solid $indigo-800; + border-radius: 0; background-color: $gray-900; cursor: default; color: $indigo-200; @@ -235,7 +238,7 @@ .fa-circle { line-height: 18px; display: inline-block; - + .icon-light { stroke: $gray-800; } @@ -245,6 +248,7 @@ .command-state-pending + .command-progress { height: 2px; + background-color: $gray-900; span { animation-fill-mode: forwards; @@ -271,6 +275,7 @@ &:not(.command-type-system) { border-left: $warn-border; + border-radius: 0 4px 4px 0; } .command-number-column, @@ -294,7 +299,8 @@ color: $err-header-text; &:not(.command-type-system) { - border-left: $err-border; + border-left: 2px solid transparent; + border-radius: 0 4px 4px 0; background-color: $err-header-background; &.command-is-interactive:hover { @@ -308,10 +314,6 @@ color: $err-header-text; } - .failed-indicator { - vertical-align: middle; - } - .command-group { border-color: $err-header-text; @include nested-command-dashes($err-header-text); @@ -434,7 +436,7 @@ padding-top: 4px; svg { - color: rgba($gray-600, .25); + color: rgba($gray-600, 0.25); color: $gray-600; vertical-align: text-top; } @@ -453,7 +455,7 @@ display: inline-block; margin-left: 2px; } - + > *:first-child { margin-left: 0; } @@ -467,8 +469,6 @@ color: $pinned; font-size: 12px; line-height: 1; - margin-top: -1px; - margin-left: 12px; outline: none; text-align: right; width: 15px; @@ -500,16 +500,12 @@ .command-expander-column { @extend %command-expander-base; - padding: 4px 5px 4px 11px; - width: 25px; - - .command-expander { - margin-top: 5px; - } + height: 28px; + width: 24px; + justify-content: center; + align-items: center; } - - .command-expander-column-group { @extend %command-expander-base; @include group-indent-width; @@ -526,6 +522,7 @@ .command-is-pinned { background: $indigo-1000; border-left: 2px solid $pinned; + border-radius: 0 4px 4px 0; &, &:hover { @@ -535,6 +532,7 @@ &:hover { background: $indigo-900; border-left: 2px solid $pinned; + border-radius: 0 4px 4px 0; } } @@ -546,6 +544,6 @@ box-shadow: inset 0 1px 1px rgba($white, 0.05); min-height: 28px; padding: 9px; + margin: 8px 0; } } - diff --git a/packages/reporter/src/errors/errors.scss b/packages/reporter/src/errors/errors.scss index c9bd40049ddf..54d1b318d27e 100644 --- a/packages/reporter/src/errors/errors.scss +++ b/packages/reporter/src/errors/errors.scss @@ -18,7 +18,9 @@ $code-border-radius: 4px; } } - p, ul, ol { + p, + ul, + ol { font-size: 1.1em; } @@ -43,7 +45,7 @@ $code-border-radius: 4px; } ul li { - list-style: disc + list-style: disc; } ol li { @@ -54,12 +56,12 @@ $code-border-radius: 4px; .recovered-test-err { .runnable-err-header, .runnable-err-body { - padding-left: 38px; + padding-left: 48px; display: flex; .err-group-block { @include group-indent-width; - + border-left: 1px dotted $err-header-text; border-image-slice: 0 0 0 1; border-image-source: repeating-linear-gradient(0deg, transparent, $err-header-text, $err-header-text 2px); @@ -68,9 +70,8 @@ $code-border-radius: 4px; width: 16px; min-width: 16px; } - } - } - + } + } .runnable-err-content { padding: 0 12px 0 0; @@ -79,8 +80,12 @@ $code-border-radius: 4px; .runnable-err-content { width: 100%; - overflow: scroll; - padding: 0 18px; + + .is-open { + > .runnable-err-stack-expander .err-collapsible-indicator { + transform: rotate(90deg); + } + } } .studio-err-wrapper { @@ -89,7 +94,8 @@ $code-border-radius: 4px; .runnable-err { background-color: $err-background; - border-left: $err-border; + border-left: 2px solid transparent; + border-radius: 4px; clear: both; color: $err-text; font-family: $monospace; @@ -114,14 +120,13 @@ $code-border-radius: 4px; &.runnable-err-icon-group { width: auto; } - + svg { color: $red-400; - align-self: center + align-self: center; } } - .runnable-err-name { @include command-info-padding; @@ -145,7 +150,7 @@ $code-border-radius: 4px; font-family: $font-system; font-size: 14px; font-weight: 400; - padding: 10px 0; + padding: 8px 14px; code { background-color: rgba($black, 0.2); @@ -164,9 +169,9 @@ $code-border-radius: 4px; .runnable-err-stack-expander { align-items: center; - border-top: 1px dashed rgba($red-400, 0.1); + border-top: 1px solid #4b364c40; display: flex; - padding: 10px 0; + padding: 16px 14px; flex-wrap: wrap-reverse; .collapsible-header { flex-grow: 1; @@ -184,30 +189,24 @@ $code-border-radius: 4px; .collapsible-header-text { color: $red-100; } - .collapsible-indicator { - .icon-dark { - stroke: $red-200; - } + .err-collapsible-indicator path { + stroke: $red-200; } } - div { + .collapsible-header-inner { cursor: pointer; outline: none; - padding: 6px 0; + padding: 0 !important; width: 100%; .collapsible-header-text { color: $red-300; font-size: 14px; font-weight: 500; - } - - .collapsible-indicator { - line-height: 18px; - .icon-dark { - stroke: $red-400; - } + display: inline-flex; + gap: 4px; + align-items: center; } } } @@ -264,7 +263,7 @@ $code-border-radius: 4px; // ensure empty lines still take up vertical space &:empty:before { - content: ' '; + content: " "; } } } @@ -299,4 +298,3 @@ $code-border-radius: 4px; } } } - diff --git a/packages/reporter/src/errors/prism.scss b/packages/reporter/src/errors/prism.scss index 841e65a2dee0..a18f1ddc7929 100644 --- a/packages/reporter/src/errors/prism.scss +++ b/packages/reporter/src/errors/prism.scss @@ -1,15 +1,19 @@ @import "../../node_modules/prismjs/themes/prism"; @import "../../node_modules/prismjs/plugins/line-highlight/prism-line-highlight"; -code[class*="language-"], -pre[class*="language-"] { +code.language-js, +code.language-ts, +pre.language-js, +pre.language-ts { color: $gray-400; margin: 0; text-shadow: $black 0px 1px; } -:not(pre) > code[class*="language-"], -pre[class*="language-"] { +:not(pre) > code.language-js, +:not(pre) > code.language-ts, +pre.language-js, +pre.language-ts { background: $gray-1100; } diff --git a/packages/reporter/src/errors/test-error.tsx b/packages/reporter/src/errors/test-error.tsx index a4cbb6d1224e..57acf85d2dfa 100644 --- a/packages/reporter/src/errors/test-error.tsx +++ b/packages/reporter/src/errors/test-error.tsx @@ -16,6 +16,7 @@ import { formattedMessage } from '../commands/command' import WarningIcon from '@packages/frontend-shared/src/assets/icons/warning_x8.svg' import TerminalIcon from '@packages/frontend-shared/src/assets/icons/technology-terminal_x16.svg' +import { IconChevronRightMedium } from '@cypress-design/react-icon' interface DocsUrlProps { url: string | string[] @@ -70,6 +71,12 @@ const TestError: React.FC = ({ err, groupLevel = 0, testId, comm } } + const _header = + <> + + Stack trace + + return (
    @@ -90,25 +97,26 @@ const TestError: React.FC = ({ err, groupLevel = 0, testId, comm
    {codeFrame && } {err.stack && - -
    events.emit('show:error', { err, groupLevel, testId, commandId }))} - role='button' - tabIndex={0} - > -
    Print to console
    -
    - - } - contentClass='runnable-err-stack-trace' - > - -
    + +
    events.emit('show:error', { err, groupLevel, testId, commandId }))} + role='button' + tabIndex={0} + > +
    Print to console
    +
    + + } + contentClass='runnable-err-stack-trace' + > + +
    }
    diff --git a/packages/reporter/src/header/DebugDismiss.scss b/packages/reporter/src/header/DebugDismiss.scss index 237763b33db5..f83efe2be234 100644 --- a/packages/reporter/src/header/DebugDismiss.scss +++ b/packages/reporter/src/header/DebugDismiss.scss @@ -1,15 +1,16 @@ .debug-dismiss { - display: flex !important; + display: flex; align-items: center; - font-size: 12px !important; - font-weight: 600 !important; - line-height: 16px !important; - color: #9AA2FC !important; - border: solid 1px #9AA2FC !important; - border-radius: 16px !important; + font-size: 12px; + font-weight: 600; + line-height: 16px; + color: $indigo-300; + border: solid 1px $indigo-300; + border-radius: 16px; gap: 4px; + padding: 4px 8px; .delete-icon path { - fill: #9AA2FC + fill: $indigo-300; } } \ No newline at end of file diff --git a/packages/reporter/src/header/controls.tsx b/packages/reporter/src/header/controls.tsx index 64736b8b48f6..e9aea6420e1a 100755 --- a/packages/reporter/src/header/controls.tsx +++ b/packages/reporter/src/header/controls.tsx @@ -10,10 +10,11 @@ import type { AppState } from '../lib/app-state' import ChevronDownIcon from '@packages/frontend-shared/src/assets/icons/chevron-down-small_x16.svg' import ChevronUpIcon from '@packages/frontend-shared/src/assets/icons/chevron-up-small_x16.svg' -import NextIcon from '@packages/frontend-shared/src/assets/icons/action-next_x16.svg' -import PlayIcon from '@packages/frontend-shared/src/assets/icons/action-play_x16.svg' -import RestartIcon from '@packages/frontend-shared/src/assets/icons/action-restart_x16.svg' -import StopIcon from '@packages/frontend-shared/src/assets/icons/action-stop_x16.svg' +import { IconActionNext, IconActionPlayLarge, IconActionRestart, IconActionStopCircle } from '@cypress-design/react-icon' + +const iconStrokeColor = 'gray-500' + +const iconFillColor = 'gray-900' const ifThen = (condition: boolean, component: React.ReactNode) => ( condition ? component : null @@ -32,7 +33,7 @@ const Controls: React.FC = observer(({ events = defaultEvents, appState } } return ( -
    +
    Open Testing Preferences

    } className='cy-tooltip'>
    - {ifThen(appState.isPaused, ( - Resume C

    } className='cy-tooltip'> - -
    - ))} - {ifThen(appState.isRunning && !appState.isPaused, ( - Stop Running S

    } className='cy-tooltip' visible={appState.studioActive ? false : null}> - -
    - ))} - {ifThen(!appState.isRunning, ( - Run All Tests R

    } className='cy-tooltip'> - -
    - ))} - {ifThen(!!appState.nextCommandName, ( - Next [N]:{appState.nextCommandName}

    } className='cy-tooltip'> - -
    - ))} +
    + {ifThen(appState.isPaused, ( + Resume C

    } className='cy-tooltip'> + +
    + ))} + {ifThen(appState.isRunning && !appState.isPaused, ( + Stop Running S

    } className='cy-tooltip' visible={appState.studioActive ? false : null}> + +
    + ))} + {ifThen(!appState.isRunning, ( + Run All Tests R

    } className='cy-tooltip'> + +
    + ))} + {ifThen(!!appState.nextCommandName, ( + Next [N]:{appState.nextCommandName}

    } className='cy-tooltip'> + +
    + ))} +
    ) }) diff --git a/packages/reporter/src/header/header.scss b/packages/reporter/src/header/header.scss index 82d1417cc763..a51d4d3a5f26 100644 --- a/packages/reporter/src/header/header.scss +++ b/packages/reporter/src/header/header.scss @@ -1,87 +1,39 @@ +@import "../lib/mixins.scss"; + $color-transition: color 150ms ease-out; .reporter { - header { + .spec-container { background-color: $gray-1100; display: flex; flex-shrink: 0; flex-wrap: wrap; - gap: 8px; + gap: 13px; font-family: $font-system; min-height: $header-height; outline: 0; overflow: hidden; - padding: 20px 16px; + padding: 16px; width: 100%; z-index: 1; + align-items: center; .spacer { flex-grow: 2; } - - button { - background-color: transparent; - border-color: transparent; - border-radius: 0; - display: inline-block; - font-weight: 300; - line-height: 26px; - outline: 0; - padding: 0 8px; - text-align: center; - - &:hover { - background-color: $gray-900; - } - - &[disabled], - &[disabled]:hover, - &[disabled]:active { - background: none; - box-shadow: none; - color: $gray-500; - } - } } - .toggle-specs-wrapper { - display: flex; - height: 24px; - - button { - color: $gray-700; - font-size: 16px; - font-weight: 300; - padding-left: 0; - padding-right: 8px; - transition: $color-transition; - width: auto !important; - - &:focus, - &:hover { - background-color: initial; - - svg { - color: $gray-400; - transition: $color-transition; - } - - .toggle-specs-text { - color: $gray-400; - transition: $color-transition; - } - } - - .toggle-specs-text { - color: $gray-500; - transition: $color-transition; - } - - svg { - margin-right: 8px; - margin-bottom: -2px; - } - } + .statsAndControls { + display: inline-flex; + width: 100%; + justify-content: space-between; + align-items: center; + padding: 0 16px; + border: 1px solid $gray-900; + border-left: none; + flex-wrap: wrap; + gap: 4px; + min-height: 64px; } .stats { @@ -90,29 +42,21 @@ $color-transition: color 150ms ease-out; border-radius: 4px; display: flex; flex-wrap: wrap; - height: 24px; + min-height: 24px; justify-content: space-between; - padding: 0 4px; - min-width: 124px; li { display: flex; - font-size: 12px; - font-weight: 600; + align-items: center; + font-size: 14px; + font-weight: 700; list-style-type: none; - padding: 0 4px; - - svg { - margin-right: 2px; - } + padding: 2px 8px; + gap: 4px; + line-height: 20px; .num { - color: $white; - line-height: 12px; - vertical-align: text-top; - min-width: 16px; - display: inline-block; - text-align: center; + color: $gray-400; &.empty { color: $gray-800; @@ -121,19 +65,52 @@ $color-transition: color 150ms ease-out; } } - .controls { + @mixin control-container-styles($size) { + height: $size; + + .testing-preferences-toggle { + height: $size; + width: $size; + } + + .controls { + height: $size; + + span button { + width: $size; + } + } + } + + .controls-container-studio { + // TODO: https://github.com/cypress-io/cypress-services/issues/10425 change this to 32px for the studio redesign + @include control-container-styles(24px); + } + + .controls-container { + @include control-container-styles(24px); + } + + .controls-container, + .controls-container-studio { + display: inline-flex; align-items: center; - border: 1px solid $gray-900; - border-radius: 4px; - display: flex; - flex-wrap: wrap; justify-content: center; - height: 24px; + gap: 4px; + flex-wrap: wrap; + + span { + height: 100%; + } .testing-preferences-toggle { - border-left: none; - color: $gray-700; - margin-left: 0; + display: flex; + justify-content: center; + align-items: center; + height: 100%; + color: $gray-500; + border: 1px solid $gray-900; + border-radius: 4px; &.open { background-color: $gray-900; @@ -141,20 +118,136 @@ $color-transition: color 150ms ease-out; } } - span { - height: 100%; + .controls { + align-items: center; + border: 1px solid $gray-900; + border-radius: 4px; + display: flex; + justify-content: center; + + span { + button { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + color: $gray-400; + padding: 0px; + border-left: 1px solid $gray-900; + margin-left: -1px; + } - button { - display: flex; - justify-content: center; - align-items: center; - height: 100%; - color: $gray-400; - width: 31px; - padding: 0px; - border-left: 1px solid $gray-900; - margin-left: -1px; + &:first-child { + button { + border-left: none; + } + } + + &:last-child { + button { + border-right: none; + } + } + } + } + } + + .runnable-header { + @include inner-header; + display: flex; + align-items: center; + gap: 8px; + padding: 0; + position: unset; + line-height: 20px; + min-width: 0; + align-items: center; + flex: 1; + + .runnable-header-file-name { + display: inline-flex; + align-items: center; + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + position: relative; + background: $gray-1100; + + &:after { + content: none; + } + + .spec-name { + color: $white; + font-weight: 500; } + + .spec-file-extension { + color: $gray-300; + font-weight: 300; + } + + .button-hover-shadow { + @include button-hover-shadow( + $linear-gradient: linear-gradient(90deg, rgba(22, 24, 39, 0) 5.39%, $gray-1100 31.97%), + $width: 160px + ); + } + + .open-in-ide-button { + @include new-test-button; + } + + &:hover, + &:focus-visible { + .open-in-ide-button { + opacity: 1; + } + + .button-hover-shadow { + opacity: 1; + } + } + } + + span > span > a > svg { + margin-bottom: -2px; + margin-right: 8px; + } + + a, + a:active, + a:focus, + a:hover { + color: $gray-500; + font-weight: 300; + + strong { + color: $white; + font-weight: 500; + } + } + + .duration { + border: 1px solid $gray-900; + border-radius: 18px; + color: $gray-400; + font-size: 12px; + font-weight: 500; + line-height: 20px; + padding: 0 8px; + font-variant-numeric: tabular-nums; + } + } + + .toggle-specs-wrapper { + .toggle-specs-button { + padding: 0; + width: 32px; + justify-content: center; + color: $gray-500; } } } diff --git a/packages/reporter/src/header/header.tsx b/packages/reporter/src/header/header.tsx index 678d347f21de..7d97924865a9 100644 --- a/packages/reporter/src/header/header.tsx +++ b/packages/reporter/src/header/header.tsx @@ -2,50 +2,56 @@ import { observer } from 'mobx-react' import React from 'react' // @ts-ignore import Tooltip from '@cypress/react-tooltip' - -import MenuExpandRightIcon from '@packages/frontend-shared/src/assets/icons/menu-expand-right_x16.svg' - +import Button from '@cypress-design/react-button' import defaultEvents, { Events } from '../lib/events' import type { AppState } from '../lib/app-state' import { action } from 'mobx' - -import Controls from './controls' -import Stats from './stats' import type { StatsStore } from './stats-store' -import { DebugDismiss } from './DebugDismiss' import type { RunnablesStore } from '../runnables/runnables-store' +import RunnableHeader from '../runnables/runnable-header' +import MenuExpandRightIcon from '@packages/frontend-shared/src/assets/icons/menu-expand-right_x16.svg' +import Stats from './stats' +import Controls from './controls' export interface ReporterHeaderProps { appState: AppState events?: Events statsStore: StatsStore runnablesStore: RunnablesStore + spec?: Cypress.Cypress['spec'] } -const Header: React.FC = observer(({ appState, events = defaultEvents, statsStore, runnablesStore }: ReporterHeaderProps) => ( -
    - {appState.isSpecsListOpen ? 'Collapse' : 'Expand'} Specs List F

    } wrapperClassName='toggle-specs-wrapper' className='cy-tooltip'> - -
    -
    - {runnablesStore.testFilter && runnablesStore.totalTests > 0 && } - - +const Header: React.FC = observer(({ appState, events = defaultEvents, statsStore, runnablesStore, spec }: ReporterHeaderProps) => { + return
    +
    + {appState.isSpecsListOpen ? 'Collapse' : 'Expand'} Specs List F

    } wrapperClassName='toggle-specs-wrapper' className='cy-tooltip'> +
    + +
    +
    + {spec && } +
    +
    + + +
    -)) +}) Header.displayName = 'Header' diff --git a/packages/reporter/src/header/stats.tsx b/packages/reporter/src/header/stats.tsx index 0273fd5cf593..baed8414007c 100644 --- a/packages/reporter/src/header/stats.tsx +++ b/packages/reporter/src/header/stats.tsx @@ -3,10 +3,7 @@ import { observer } from 'mobx-react' import React from 'react' import type { StatsStore } from './stats-store' - -import FailedIcon from '@packages/frontend-shared/src/assets/icons/status-failed_x12.svg' -import PassedIcon from '@packages/frontend-shared/src/assets/icons/status-passed_x12.svg' -import PendingIcon from '@packages/frontend-shared/src/assets/icons/status-pending_x12.svg' +import { IconStatusFailedSimple, IconStatusPassedSimple, IconStatusSkippedOutline } from '@cypress-design/react-icon' const count = (num: number) => num > 0 ? num : '--' @@ -17,17 +14,17 @@ interface Props { const Stats: React.FC = observer(({ stats }: Props) => (
    • -
    • -
    • -
    • diff --git a/packages/reporter/src/hooks/hooks.scss b/packages/reporter/src/hooks/hooks.scss index 84b011bcaf0f..0b99728aa351 100644 --- a/packages/reporter/src/hooks/hooks.scss +++ b/packages/reporter/src/hooks/hooks.scss @@ -1,35 +1,55 @@ +@import "../lib/mixins.scss"; + .reporter { .hooks-container { - .hook-item { - margin-bottom: 5px; - - &:last-of-type { - margin-bottom: 0; - } - } - .hook-header { font-family: $font-system; display: flex; width: 100%; + align-items: center; + padding: 0 8px; + + .button-hover-shadow { + @include button-hover-shadow(); + } - &:hover { - background-color: $gray-900; + &:hover, + &:focus-visible { + background-color: $gray-950; .hook-open-in-ide { - cursor: pointer; - display: block; + opacity: 1; + + &:hover, + &:focus-visible { + background-color: $gray-950; + + + .button-hover-shadow { + @include button-hover-shadow(); + opacity: 1; + } + } + } + + .button-hover-shadow { + opacity: 1; } } .collapsible-header { text-transform: uppercase; color: $gray-400; - display: inline-block; + display: inline-flex; + align-items: center; flex-grow: 1; - font-size: 11px; + font-size: 12px; cursor: pointer; - padding: 4px 0; + width: 100%; + + .collapsible-header-inner { + padding: 6px 0 !important; + width: 100%; + } &:focus { outline: none; @@ -38,6 +58,11 @@ > .collapsible-header-inner:focus { outline: 0; } + + .collapsible-header-inner { + display: flex; + align-items: center; + } } .hook-failed-message { @@ -45,24 +70,43 @@ } .hook-open-in-ide { + opacity: 0; + position: absolute; + top: 0; + right: 0; + cursor: pointer; + display: inline-flex; align-items: center; - color: $gray-400; - display: none; - padding: 4px; + gap: 4px; + color: $gray-300; + background-color: $gray-950; + border: 1px solid $gray-900; + z-index: 1; - &:hover, &:focus { - background-color: rgba($gray-700, 0.2); + &:hover, + &:focus-visible { + opacity: 1; + border: 1px solid rgb(255 255 255 / 0.6); outline: none; text-decoration: none; } - svg { - vertical-align: middle; + &:hover { + background-color: $gray-950; + } + + &:focus-visible { + background-color: $reporter-section-background; - path:nth-child(2) { - fill: $gray-800; + + .button-hover-shadow { + opacity: 1; + background: linear-gradient(90deg, rgba(37, 40, 60, 0) -9.8%, $reporter-section-background 19.52%); } } + + svg { + flex-shrink: 0; + } } } } diff --git a/packages/reporter/src/hooks/hooks.tsx b/packages/reporter/src/hooks/hooks.tsx index 15b099ccebc1..ce947e986277 100644 --- a/packages/reporter/src/hooks/hooks.tsx +++ b/packages/reporter/src/hooks/hooks.tsx @@ -2,17 +2,13 @@ import cs from 'classnames' import _ from 'lodash' import { observer } from 'mobx-react' import React from 'react' -import type { FileDetails } from '@packages/types' - import appState, { AppState } from '../lib/app-state' import Command from '../commands/command' import Collapsible from '../collapsible/collapsible' import type HookModel from './hook-model' import type { HookName } from './hook-model' - import ArrowRightIcon from '@packages/frontend-shared/src/assets/icons/arrow-right_x16.svg' -import OpenIcon from '@packages/frontend-shared/src/assets/icons/technology-code-editor_x16.svg' -import OpenFileInIDE from '../lib/open-file-in-ide' +import { OpenFileInIDEButton } from '../OpenFileInIDEButton' export interface HookHeaderProps { model: HookModel @@ -26,18 +22,6 @@ const HookHeader = ({ model, number }: HookHeaderProps) => ( ) -export interface HookOpenInIDEProps { - invocationDetails: FileDetails -} - -const HookOpenInIDE = ({ invocationDetails }: HookOpenInIDEProps) => { - return ( - - Open in IDE - - ) -} - const StudioNoCommands = () => (
    • @@ -66,9 +50,13 @@ export interface HookProps { const Hook: React.FC = observer(({ model, showNumber, scrollIntoView }: HookProps) => (
    • } + header={ + <> + + {model.invocationDetails && Cypress.testingType !== 'component' && } + + } headerClass='hook-header' - headerExtras={model.invocationDetails && Cypress.testingType !== 'component' && } isOpen >
        diff --git a/packages/reporter/src/instruments/instruments.scss b/packages/reporter/src/instruments/instruments.scss index bf222b365f6f..aae9bf5def50 100644 --- a/packages/reporter/src/instruments/instruments.scss +++ b/packages/reporter/src/instruments/instruments.scss @@ -10,6 +10,10 @@ padding: 0 2px 0 12px; } + > .hooks-container > .hook-item > .collapsible { + margin-bottom: 4px; + } + .instrument-content h3, h2, h1:first-child { @@ -21,7 +25,7 @@ td { font-family: $monospace; - font-size: 11px; + font-size: 12px; } } diff --git a/packages/reporter/src/lib/base.scss b/packages/reporter/src/lib/base.scss index 04dcdb629809..ff00d198306a 100644 --- a/packages/reporter/src/lib/base.scss +++ b/packages/reporter/src/lib/base.scss @@ -1,4 +1,92 @@ -body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img,a img{border:none;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:$white;} +body, +div, +dl, +dt, +dd, +ul, +ol, +li, +h1, +h2, +h3, +h4, +h5, +h6, +pre, +code, +form, +fieldset, +legend, +input, +button, +textarea, +p, +blockquote, +th, +td { + margin: 0; + padding: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +fieldset, +img, +a img { + border: none; +} +address, +caption, +cite, +code, +dfn, +em, +strong, +th, +var, +optgroup { + font-style: inherit; + font-weight: inherit; +} +del, +ins { + text-decoration: none; +} +li { + list-style: none; +} +caption, +th { + text-align: left; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: 100%; + font-weight: normal; +} +q:before, +q:after { + content: ""; +} +abbr, +acronym { + border: 0; + font-variant: normal; +} +sup { + vertical-align: baseline; +} +sub { + vertical-align: baseline; +} +legend { + color: $white; +} .reporter { background-color: $gray-1100; @@ -13,10 +101,6 @@ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input right: 0; top: 0; - * { - box-sizing: border-box; - } - &, input, textarea { @@ -56,25 +140,12 @@ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input font-style: italic; } - button { - background: $black; - border: none; - border-radius: 2px; - color: $gray-200; - cursor: pointer; - display: inline-block; - font-size: 14px; - font-weight: 500; - line-height: 1.4; - padding: 10px 16px; - text-align: center; - touch-action: manipulation; - vertical-align: middle; - white-space: nowrap; - user-select: none; - } - - h1, h2, h3, h4, h5, h6 { + h1, + h2, + h3, + h4, + h5, + h6 { font-weight: 300; line-height: 1.1; } @@ -136,13 +207,13 @@ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input label { background-color: $gray-100; - border-radius: .25em; + border-radius: 0.25em; color: $black; display: inline; font-size: 75%; font-weight: bold; line-height: 1; - padding: .2em .6em .3em; + padding: 0.2em 0.6em 0.3em; text-align: center; white-space: nowrap; vertical-align: baseline; diff --git a/packages/reporter/src/lib/mixins.scss b/packages/reporter/src/lib/mixins.scss index a8f51eb474f8..010c5b296e88 100644 --- a/packages/reporter/src/lib/mixins.scss +++ b/packages/reporter/src/lib/mixins.scss @@ -1,7 +1,8 @@ @mixin inner-header { background: $gray-1100; display: block; - font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", + sans-serif; font-size: 14px; line-height: 24px; overflow-wrap: break-word; @@ -14,7 +15,7 @@ &:before, &:after { background-color: $gray-900; - content: ''; + content: ""; left: 16px; position: absolute; width: calc(100% - 32px); @@ -32,12 +33,13 @@ @mixin gutter-alignment { flex-shrink: 0; - min-height: 1px; // because some numbers are empty - max-height: 28px; // because some numbers are empty + height: 28px; // because some numbers are empty padding-top: 4px; padding-bottom: 4px; - text-align: right; width: 24px; + display: flex; + align-items: center; + justify-content: center; } @mixin command-info-padding { @@ -50,4 +52,39 @@ width: 18px; min-width: 18px; max-width: 18px; +} + +@mixin new-test-button { + opacity: 0; + position: absolute; + top: 0; + right: 0; + gap: 4px; + color: $gray-300; + background: inherit; + display: flex; + align-items: center; + flex-shrink: 0; + z-index: 1; + + &:hover, + &:focus-visible { + opacity: 1 !important; + + + .button-hover-shadow { + opacity: 1 !important; + } + } +} + + +@mixin button-hover-shadow($linear-gradient: linear-gradient(90deg, rgba(37, 40, 60, 0) -9.8%, $gray-950 19.52%), $width: 130px) { + opacity: 0; + position: absolute; + width: $width; + height: 100%; + top: 0; + right: 0; + background: $linear-gradient; + z-index: 0; } \ No newline at end of file diff --git a/packages/reporter/src/lib/state-icon.tsx b/packages/reporter/src/lib/state-icon.tsx index 1da90dafa3fa..66d78f347831 100644 --- a/packages/reporter/src/lib/state-icon.tsx +++ b/packages/reporter/src/lib/state-icon.tsx @@ -3,30 +3,29 @@ import { observer } from 'mobx-react' import React from 'react' import type { TestState } from '@packages/types' -import FailedIcon from '@packages/frontend-shared/src/assets/icons/status-failed_x12.svg' -import PassedIcon from '@packages/frontend-shared/src/assets/icons/status-passed_x12.svg' -import PendingIcon from '@packages/frontend-shared/src/assets/icons/status-pending_x12.svg' -import ProcessingIcon from '@packages/frontend-shared/src/assets/icons/status-processing_x12.svg' -import RunningIcon from '@packages/frontend-shared/src/assets/icons/status-running_x12.svg' import WandIcon from '@packages/frontend-shared/src/assets/icons/object-magic-wand-dark-mode_x16.svg' +import { IconStatusFailedSimple, IconStatusPassedSimple, IconStatusQueuedOutline, IconStatusQueuedSimple, IconStatusRunningOutline, IconStatusRunningSimple, IconStatusSkippedOutline, IconStatusSkippedSimple } from '@cypress-design/react-icon' -interface Props extends React.HTMLProps { +interface Props extends React.SVGProps { state: TestState isStudio?: boolean + iconSize?: '8' | '12' | '16' } const StateIcon: React.FC = observer((props: Props) => { - const { state, isStudio, ...rest } = props + const { state, isStudio, ref, iconSize, ...rest } = props if (state === 'active') { return ( - + iconSize === '8' ? + : + ) } if (state === 'failed') { return ( - + ) } @@ -38,24 +37,38 @@ const StateIcon: React.FC = observer((props: Props) => { } return ( - + ) } + // pending is really skipped if (state === 'pending') { return ( - + iconSize === '8' ? + : + ) } + // processing is really queued if (state === 'processing') { return ( - + iconSize === '8' ? + : + ) } return ( - + iconSize === '8' ? + : + ) }) diff --git a/packages/reporter/src/lib/util.ts b/packages/reporter/src/lib/util.ts index dbef553a2b60..226db711e508 100644 --- a/packages/reporter/src/lib/util.ts +++ b/packages/reporter/src/lib/util.ts @@ -1,12 +1,5 @@ import type { KeyboardEvent } from 'react' -const INDENT_BASE = 5 -const INDENT_AMOUNT = 15 - -function indent (level: number) { - return INDENT_BASE + level * INDENT_AMOUNT -} - // Returns a keyboard handler that invokes the provided function when either enter or space is pressed const onEnterOrSpace = (f: (() => void)) => { return (e: KeyboardEvent) => { @@ -74,6 +67,5 @@ const getFilenameParts = (spec: string): [string, string] => { export { formatDuration, getFilenameParts, - indent, onEnterOrSpace, } diff --git a/packages/reporter/src/main.scss b/packages/reporter/src/main.scss index 6dd8c7d8c5c9..d4aea12f9af8 100644 --- a/packages/reporter/src/main.scss +++ b/packages/reporter/src/main.scss @@ -1,3 +1,8 @@ +// Tailwind CSS +@tailwind base; +@tailwind components; +@tailwind utilities; + // this file is used when developing the reporter in isolation via cypress tests // if you update this file, also update main-runner.scss @import 'lib/variables'; diff --git a/packages/reporter/src/main.tsx b/packages/reporter/src/main.tsx index fd48db362bb2..4758477bc1a2 100644 --- a/packages/reporter/src/main.tsx +++ b/packages/reporter/src/main.tsx @@ -47,12 +47,12 @@ export interface BaseReporterProps { runnerStore: MobxRunnerStore } -export interface SingleReporterProps extends BaseReporterProps{ +export interface SingleReporterProps extends BaseReporterProps { runMode?: 'single' } // In React Class components (now deprecated), we used to use appState as a default prop. Now since defaultProps are not supported in functional components, we can use ES6 default params to accomplish the same thing -const Reporter: React.FC = observer(({ appState = appStateDefault, runner, className, error, runMode = 'single', studioEnabled, autoScrollingEnabled, isSpecsListOpen, resetStatsOnSpecChange, renderReporterHeader = (props: ReporterHeaderProps) =>
        , runnerStore }) => { +const Reporter: React.FC = observer(({ appState = appStateDefault, runner, className, error, runMode = 'single', studioEnabled, autoScrollingEnabled, isSpecsListOpen, resetStatsOnSpecChange, renderReporterHeader = (props: ReporterHeaderProps) =>
        , runnerStore }) => { const previousSpecRunId = usePrevious(runnerStore.specRunId) const [isMounted, setIsMounted] = useState(false) const [isInitialized, setIsInitialized] = useState(false) @@ -101,7 +101,7 @@ const Reporter: React.FC = observer(({ appState = appStateD runnablesStore.setRunningSpec(runnerStore.spec.relative) if ( resetStatsOnSpecChange && - runnerStore.specRunId !== previousSpecRunId + runnerStore.specRunId !== previousSpecRunId ) { statsStore.reset() } @@ -112,7 +112,7 @@ const Reporter: React.FC = observer(({ appState = appStateD 'studio-active': appState.studioActive, 'mounted': isMounted, })}> - {renderReporterHeader({ appState, statsStore, runnablesStore })} + {renderReporterHeader({ appState, statsStore, runnablesStore, spec: runnerStore.spec })} {appState?.isPreferencesMenuOpen ? ( ) : ( diff --git a/packages/reporter/src/preferences/testing-preferences.scss b/packages/reporter/src/preferences/testing-preferences.scss index a6ba68c2bd80..67c3ad6219db 100644 --- a/packages/reporter/src/preferences/testing-preferences.scss +++ b/packages/reporter/src/preferences/testing-preferences.scss @@ -4,6 +4,10 @@ font-size: 16px; font-weight: 400; color: $gray-700; + + &::before { + width: 0; + } } .testing-preference { @@ -19,4 +23,4 @@ margin-bottom: 8px; } } -} +} \ No newline at end of file diff --git a/packages/reporter/src/runnables/runnable-and-suite.tsx b/packages/reporter/src/runnables/runnable-and-suite.tsx index ec006923a97e..1622e97c4fcc 100644 --- a/packages/reporter/src/runnables/runnable-and-suite.tsx +++ b/packages/reporter/src/runnables/runnable-and-suite.tsx @@ -1,19 +1,24 @@ import cs from 'classnames' import _ from 'lodash' import { observer } from 'mobx-react' -import React, { MouseEvent, useCallback } from 'react' - -import { indent } from '../lib/util' +import React, { MouseEvent, useCallback, useMemo } from 'react' import appState, { AppState } from '../lib/app-state' import events, { Events } from '../lib/events' import Test from '../test/test' -import Collapsible from '../collapsible/collapsible' +import Collapsible, { CollapsibleHeaderComponentProps } from '../collapsible/collapsible' import type SuiteModel from './suite-model' import type TestModel from '../test/test-model' -import { LaunchStudioIcon } from '../components/LaunchStudioIcon' +import { IconActionAddMedium, IconChevronDownMedium, IconChevronRightMedium, IconObjectStackFailed, IconObjectStackPassed, IconObjectStackQueued, IconObjectStackRunning, IconObjectStackSkipped, WindiColor } from '@cypress-design/react-icon' +import Button from '@cypress-design/react-button' +import { RunnableArray } from './runnables-store' + +// should only show connection dots if the current runnable is a test and the next runnable is a test and is not the last runnable +export const shouldShowConnectionDots = (runnables: RunnableArray, runnable: SuiteModel | TestModel, runnableIndex: number) => { + return runnable.type === 'test' && runnableIndex !== runnables.length - 1 && runnables[runnableIndex + 1].type === 'test' +} interface SuiteProps { eventManager?: Events @@ -22,6 +27,12 @@ interface SuiteProps { canSaveStudioLogs: boolean } +const headerIconDefaultProps = { + fillColor: 'gray-900' as WindiColor, + strokeColor: 'gray-500' as WindiColor, + className: 'header-icon', +} + const Suite: React.FC = observer(({ eventManager = events, model, studioEnabled, canSaveStudioLogs }: SuiteProps) => { const _launchStudio = useCallback((e: MouseEvent) => { e.preventDefault() @@ -30,67 +41,120 @@ const Suite: React.FC = observer(({ eventManager = events, model, st eventManager.emit('studio:init:suite', model.id) }, [eventManager, model.id]) - const _header = () => ( - <> - {model.title} - {(studioEnabled && !appState.studioActive) && ( - - - - )} + const getHeaderIcon = useCallback((isOpen: boolean) => { + let headerIcon + + switch (model.state) { + case 'active': + headerIcon = + break + case 'passed': + headerIcon = + break + case 'failed': + headerIcon = + break + case 'pending': + headerIcon = + break + case 'processing': + headerIcon = + break + default: + headerIcon = <> + break + } + + return <> + {isOpen ? : } + {headerIcon} - ) + }, [model.state]) + + const HeaderComponent = useCallback(({ isOpen }: CollapsibleHeaderComponentProps) => { + return ( + <> +
        + {getHeaderIcon(isOpen)} +
        + {model.title} + {(studioEnabled && !appState.studioActive) && ( + <> + + + + )} + + ) + }, [getHeaderIcon, model.title, studioEnabled, appState.studioActive, _launchStudio]) + + const runnablesList = useMemo(() => ( +
          + {_.map(model.children, (runnable, index) => { + return () + })} +
        + ), [model.children, studioEnabled, canSaveStudioLogs]) return ( - -
          - {_.map(model.children, (runnable) => - ())} -
        -
        + // we don't want to show the collapsible if there are no tests in the suite + model.children && !model.children.some((c) => c.type === 'test') ? runnablesList : ( + + {runnablesList} + + ) ) }) Suite.displayName = 'Suite' export interface RunnableProps { + appState?: AppState model: TestModel | SuiteModel - appState: AppState studioEnabled: boolean canSaveStudioLogs: boolean + shouldShowConnectingDots: boolean } // NOTE: some of the driver tests dig into the React instance for this component // in order to mess with its internal state. converting it to a functional // component breaks that, so it needs to stay a Class-based component or // else the driver tests need to be refactored to support it being functional -const Runnable: React.FC = observer(({ appState: appStateProps = appState, model, studioEnabled, canSaveStudioLogs }) => { - return ( +const Runnable: React.FC = observer(({ appState: appStateProps = appState, model, studioEnabled, canSaveStudioLogs, shouldShowConnectingDots }) => { + return (<>
      • {model.type === 'test' ? - : } + : }
      • + {shouldShowConnectingDots &&
        } + ) }) diff --git a/packages/reporter/src/runnables/runnable-header.tsx b/packages/reporter/src/runnables/runnable-header.tsx index b47eac8a3a78..499b0ae6203a 100644 --- a/packages/reporter/src/runnables/runnable-header.tsx +++ b/packages/reporter/src/runnables/runnable-header.tsx @@ -3,16 +3,19 @@ import React, { ReactElement } from 'react' import type { StatsStore } from '../header/stats-store' import { formatDuration, getFilenameParts } from '../lib/util' -import FileNameOpener from '../lib/file-name-opener' +import { RunnablesStore } from './runnables-store' +import { DebugDismiss } from '../header/DebugDismiss' +import { OpenFileInIDEButton } from '../OpenFileInIDEButton' const renderRunnableHeader = (children: ReactElement) =>
        {children}
        interface RunnableHeaderProps { spec: Cypress.Cypress['spec'] statsStore: StatsStore + runnablesStore: RunnablesStore } -const RunnableHeader: React.FC = observer(({ spec, statsStore }) => { +const RunnableHeader: React.FC = observer(({ spec, statsStore, runnablesStore }) => { const relativeSpecPath = spec.relative if (spec.relative === '__all') { @@ -32,7 +35,7 @@ const RunnableHeader: React.FC = observer(({ spec, statsSto return ( <> - {specParts[0]}{specParts[1]} + {specParts[0]}{specParts[1]} ) } @@ -48,7 +51,11 @@ const RunnableHeader: React.FC = observer(({ spec, statsSto return renderRunnableHeader( <> - +
        + {fileDetails.displayFile || fileDetails.originalFile}{!!fileDetails.line && `:${fileDetails.line}`}{!!fileDetails.column && `:${fileDetails.column}`} + +
        + {runnablesStore.testFilter && runnablesStore.totalTests > 0 && } {Boolean(statsStore.duration) && ( {formatDuration(statsStore.duration)} )} diff --git a/packages/reporter/src/runnables/runnable-model.ts b/packages/reporter/src/runnables/runnable-model.ts index 12e2fb6fb484..f3f98acaeaa8 100644 --- a/packages/reporter/src/runnables/runnable-model.ts +++ b/packages/reporter/src/runnables/runnable-model.ts @@ -5,6 +5,7 @@ export interface RunnableProps { id: string title?: string hooks: Array + parentTitle?: string } export default class Runnable { @@ -12,6 +13,7 @@ export default class Runnable { title?: string level: number hooks: Array = [] + parentTitle?: string constructor (props: RunnableProps, level: number) { makeObservable(this, { @@ -19,11 +21,13 @@ export default class Runnable { title: observable, level: observable, hooks: observable, + parentTitle: observable, }) this.id = props.id this.title = props.title this.level = level this.hooks = props.hooks + this.parentTitle = props.parentTitle } } diff --git a/packages/reporter/src/runnables/runnables-store.ts b/packages/reporter/src/runnables/runnables-store.ts index ea04a6ff01d6..ff139c17f31a 100644 --- a/packages/reporter/src/runnables/runnables-store.ts +++ b/packages/reporter/src/runnables/runnables-store.ts @@ -126,7 +126,32 @@ export class RunnablesStore { } _createSuite (props: SuiteProps, level: number) { - const suite = new SuiteModel(props, level) + // Get parent suite titles by traversing up the queue + const parentTitles: string[] = [] + + // Find the immediate parent suite by looking for the last suite at a lower level + let parentLevel = level - 1 + + for (let i = this._runnablesQueue.length - 1; i >= 0; i--) { + const runnable = this._runnablesQueue[i] + + if ('type' in runnable && runnable.type === 'suite' && runnable.level === parentLevel && runnable.title) { + // Add this parent's title + parentTitles.unshift(runnable.title) + break + } + } + + // Combine parent titles with current suite title + const hierarchicalTitle = [...parentTitles, props.title].join(' > ') + + // Create new props with the hierarchical title + const suiteProps = { + ...props, + title: hierarchicalTitle, + } + + const suite = new SuiteModel(suiteProps, level) this._runnablesQueue.push(suite) suite.children = this._createRunnableChildren(props, ++level) diff --git a/packages/reporter/src/runnables/runnables.scss b/packages/reporter/src/runnables/runnables.scss index 784ffe7e8873..348e451d21f3 100644 --- a/packages/reporter/src/runnables/runnables.scss +++ b/packages/reporter/src/runnables/runnables.scss @@ -1,4 +1,24 @@ -.fa { &:not(.fa-spin) { animation: none; } } +@import "../lib/mixins.scss"; + +$status-border-width: 2px; +$open-test-border-padding: 3px; +$dotted-line-left-padding: 19px; + +@mixin dotted-line { + content: ""; + position: absolute; + left: $dotted-line-left-padding; + bottom: -$open-test-border-padding; + height: calc(100% - 23px); + border-left: 1px dotted $gray-800; + z-index: 1; +} + +.fa { + &:not(.fa-spin) { + animation: none; + } +} .reporter { min-height: 0; // needed for firefox or else scrolling gets funky @@ -10,14 +30,20 @@ } .wrap { - border-bottom: 1px solid $gray-900; - margin-bottom: 40px; padding-left: 0; width: 100%; + margin-bottom: 16px; + margin-top: 4px; } .runnables { padding-left: 0; + display: flex; + flex-direction: column; + + .last-test-margin-bottom { + margin-bottom: 16px; + } } .no-tests { @@ -89,45 +115,88 @@ background-color: $gray-1100; overflow: auto; line-height: 18px; - padding-left: 0; .runnable-wrapper { - border-left: 4px solid transparent; - padding: 0 0 0 4px; + border-radius: 4px; + border-left: $status-border-width solid transparent; + padding: 0; + + .button-hover-shadow { + @include button-hover-shadow( + $linear-gradient: linear-gradient(90deg, rgba(22, 24, 39, 0) 5.39%, #161827 31.97%), + $width: 130px + ); + } .collapsible-header { - &:focus { + &:hover, + &:focus-visible { + .header-icon { + display: none; + } + + .header-collapsible-indicator { + display: flex; + } + + .launch-studio-button { + opacity: 1 !important; + } + + .button-hover-shadow { + opacity: 1 !important; + } + } + + &:hover { .collapsible-header-inner { - background-color: $gray-1100; - cursor: pointer; + background-color: $gray-900; } } - .collapsible-header-inner { - &:hover { + &:focus-visible { + outline: 0; + + .collapsible-header-inner { background-color: $gray-900; + outline: 0; cursor: pointer; + + .runnable-title { + color: $indigo-300; + } } - &:focus { - outline: 0; + .header-collapsible-indicator { + path { + stroke: $indigo-300; + } } - height: 100%; - padding: 5px 15px 5px 5px; - width: 100%; + .launch-studio-button { + border-color: $gray-900; + background-color: $gray-900; + } } - } - &:hover { - .runnable-controls-studio { - opacity: 0.5; + .collapsible-header-inner { + display: inline-flex; + align-items: center; + height: 100%; + width: 100%; - &:hover { - opacity: 1; + &:focus { + outline: 0; } } } + .runnable-controls-studio { + opacity: 1; + } + + .header-collapsible-indicator { + display: none; + } } .attempt-item:hover { @@ -135,51 +204,60 @@ visibility: visible !important; } - .hooks-container, .runnable-err-wrapper { + .hooks-container, + .runnable-err-wrapper { border-color: $gray-500; } } - .runnable-state,.attempt-state { + .runnable-state, + .attempt-state { display: inline-block; line-height: 18px; margin-right: 5px; min-width: 12px; text-align: center; - font-size: 11px; + font-size: 12px; } &.suite .collapsible-indicator { - margin-left: 2px; - .icon-dark { - stroke: $gray-800; - } + flex-shrink: 0; + } + + &.test.runnable-pending, + &.test.runnable-processing { + pointer-events: none; + } + + &.runnable-processing > div > .runnable-wrapper, + &.runnable-processing > div > .runnable-instruments { + border-left: $status-border-width solid $gray-700; } &.runnable-failed > div > .runnable-wrapper, &.runnable-failed > div > .runnable-instruments { - border-left: 4px solid $fail; + border-left: $status-border-width solid $fail; } &.runnable-pending > div > .runnable-wrapper, &.runnable-pending > div > .runnable-instruments { - border-left: 4px solid $pending; + border-left: $status-border-width solid $pending; padding-bottom: 0; } &.runnable-passed > div > .runnable-wrapper, &.runnable-passed > div > .runnable-instruments { - border-left: 4px solid $pass; + border-left: $status-border-width solid $pass; } .runnable-retried > div > .runnable-wrapper, .runnable-retried > div > .runnable-instruments { - border-left: 4px solid $retried; + border-left: $status-border-width solid $retried; } - &.runnable-studio.runnable-passed > div > .runnable-wrapper, + &.runnable-studio.runnable-passed.test > div > .runnable-wrapper, &.runnable-studio.runnable-passed > div > .runnable-instruments { - border-left: 4px solid $purple-400; + border-left: $status-border-width solid $purple-400; } &.runnable-skipped > .runnable-wrapper { @@ -190,13 +268,151 @@ &.runnable-skipped > div > .runnable-wrapper, &.runnable-skipped > div > .runnable-instruments { - border-left: 4px solid $gray-500; + border: 1px solid $gray-950; + border-left: $status-border-width solid $gray-500; } - &.suite > div > .runnable-wrapper { - .runnable-title { - color: $gray-50; - font-size: 13px; + &.suite > .collapsible { + > .runnable-wrapper { + border: 0; + margin: 0; + border-radius: 0; + background-color: $gray-1100; + + .collapsible-header-text { + svg { + margin-top: 1px; + } + } + + .collapsible-header-inner { + padding: 9px 11px; + background-color: $gray-1100; + } + + .runnable-title { + color: $gray-400; + font-weight: 500; + } + + .runnable-and-suite-header-icon { + position: relative; + display: flex; + justify-content: center; + } + + .icon-dark-secondary-red-400 { + :nth-child(4) { + stroke: $red-400; + } + } + + .collapsible-header-inner { + .launch-studio-button { + @include new-test-button; + } + } + } + + &.is-open > .runnable-wrapper { + position: relative; + + &::before { + @include dotted-line; + } + } + } + + &.test { + padding: $open-test-border-padding; + + > .collapsible { + display: flex; + flex-direction: column; + + .runnable-wrapper { + background-color: $gray-1000; + + &:hover { + .collapsible-header { + border-top: 1px solid $gray-900; + border-bottom: 1px solid $gray-900; + border-right: 1px solid $gray-900; + } + + .collapsible-header-inner { + background-color: $gray-1100; + } + } + + .collapsible-header { + border: 1px solid $gray-950; + border-radius: 4px; + border-bottom-left-radius: 0; + border-top-left-radius: 0; + + .collapsible-header-text { + svg { + margin-top: 1px; + } + } + + &:focus-visible { + border-top: 1px solid $gray-800; + border-bottom: 1px solid $gray-800; + border-right: 1px solid $gray-800; + + .collapsible-header-inner { + background-color: $gray-900; + } + } + } + + .collapsible-header-inner { + background-color: $gray-1000; + width: 100%; + } + } + + &.is-open { + border-radius: 4px; + box-shadow: 0 0 0 3px #43486159; + background-color: $reporter-section-background; + + .runnable-wrapper { + border-bottom-left-radius: 0; + + .collapsible-header { + border: 1px solid $gray-800; + border-left: 1px solid transparent; + border-radius: 0 4px 0 0; + } + + .collapsible-header-inner { + background-color: $reporter-section-background; + } + } + } + + .collapsible-header-inner { + padding: 8px 8px 8px 6px; + } + } + + .runnable-commands-region { + .hooks-container { + display: flex; + gap: 4px; + flex-direction: column; + width: 100%; + } + } + + &.runnable-active > .collapsible { + > .runnable-wrapper, + > .runnable-instruments { + border-left: $status-border-width solid $indigo-400; + } } } @@ -204,8 +420,9 @@ .studio-controls { display: flex; - .studio-save, .studio-copy { - display: block; + .studio-save, + .studio-copy { + display: flex; } } } @@ -232,16 +449,11 @@ .runnable-state-icon { flex-shrink: 0; - margin-right: 5px; - margin-top: 4px; - - &.fa-spin { - .icon-light { - stroke: $gray-800; - } - } &.wand-icon { + margin-top: 4px !important; + margin-left: 4px; + .icon-light { fill: $purple-300; stroke: $purple-300; @@ -250,20 +462,34 @@ } } + .runnable-dotted-line { + height: 6px; + border-left: 1px dotted #434861; + margin: (-$open-test-border-padding) 0 (-$open-test-border-padding) 19px; + z-index: 1; + } + .runnable-instruments { - border-left: 4px solid transparent; - padding-bottom: 5px; + border-left: $status-border-width solid transparent; + border-radius: 0 4px; + } + + @mixin dotted-line { + content: ""; + position: absolute; + left: $dotted-line-left-padding; + bottom: -3px; + height: calc(100% - 23px); + border-left: 1px dotted $gray-800; + z-index: 1; } .runnable-title { + color: $white; font-family: $font-system; - font-size: 12.5px; - min-width: $reporter-contents-min-width; - white-space: pre-line; - - &:focus { - outline: 1px dotted $gray-400; - } + font-size: 14px; + display: flex; + flex-grow: 1; } .runnable-wrapper > .collapsible-header { @@ -271,24 +497,13 @@ position: relative; display: inline-flex; width: 100%; - - &:focus { - outline: 1px dotted $gray-400; - outline-offset: 3px; - } } - .suite > div .runnable-wrapper, - .test .runnable-wrapper > .collapsible-header { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; + .wrap > .runnables { + padding: 0 (8px - $open-test-border-padding); } .runnable-controls { - float: right; - height: 18px; - label { padding: 2px 4px; } @@ -301,8 +516,19 @@ } .runnable-controls-studio { - color: $purple-300; + display: flex; + height: 20px; + width: 20px; + color: $gray-500; + border: 1px solid $gray-900; + border-radius: 4px; opacity: 0; + padding-top: 2px; + padding-left: 1px; + + svg { + flex: auto; + } } } @@ -310,59 +536,26 @@ visibility: visible; } - .test .collapsible { - display: flex; - flex-direction: column; - - .runnable-wrapper .collapsible-header-inner { - width: 100%; - - .collapsible-header-text { - display: flex; - - .runnable-title { - flex-grow: 1; - padding-right: 10px; - } - } - } - } - .collapsible-header { display: flex; } - .runnable-header { - @include inner-header; - - span > span > a > svg { - margin-bottom: -2px; - margin-right: 8px; - } - - a, a:active, a:focus, a:hover { - color: $gray-700; - font-weight: 300; - strong { - font-weight: 500; - } - } + .collapsible-header-text { + display: flex; + width: 100%; + gap: 8px; + position: relative; + background: inherit; - .duration { - border: 1px solid $gray-900; - border-radius: 16px; - color: $gray-600; - float: right; - font-size: 12px; - line-height: 16px; - padding: 2px 6px; - font-variant-numeric: tabular-nums; + svg { + flex-shrink: 0; } } .studio-controls { display: none; - margin: 10px 20px 10px 0; + margin: 10px 16px; + align-items: center; button { border-radius: 5px; @@ -382,7 +575,6 @@ .studio-cancel { color: rgba($white, 0.75); cursor: pointer; - margin: 7px 0 0 5px; &:hover { text-decoration: underline; @@ -397,9 +589,10 @@ color: $indigo-500; display: none; font-size: 16px; - padding: 4px 10px 2px; + padding: 6px 10px; - &:hover, &:focus { + &:hover, + &:focus { background-color: $indigo-100; } @@ -439,7 +632,7 @@ .runnable-loading { font-family: $font-system; - + .runnable-loading-animation { display: flex; margin: 3.5rem auto 1.5rem; @@ -456,32 +649,35 @@ } div:nth-child(1) { - animation-delay:0.1s; + animation-delay: 0.1s; background: $jade-400; } div:nth-child(2) { - animation-delay:0.2s; + animation-delay: 0.2s; background: $indigo-400; } div:nth-child(3) { - animation-delay:0.3s; + animation-delay: 0.3s; background: $red-400; } div:nth-child(4) { - animation-delay:0.4s; + animation-delay: 0.4s; background: $orange-400; } div:nth-child(5) { - animation-delay:0.5s; + animation-delay: 0.5s; background: $gray-400; } @keyframes scaling { - 0%, 20%, 80%, 100% { + 0%, + 20%, + 80%, + 100% { opacity: 100%; transform: scale(0.5); } diff --git a/packages/reporter/src/runnables/runnables.tsx b/packages/reporter/src/runnables/runnables.tsx index b343bc96771d..a8fd48652011 100644 --- a/packages/reporter/src/runnables/runnables.tsx +++ b/packages/reporter/src/runnables/runnables.tsx @@ -5,10 +5,9 @@ import React, { MouseEvent, useCallback, useEffect, useRef } from 'react' import events, { Events } from '../lib/events' import { RunnablesError, RunnablesErrorModel } from './runnable-error' -import Runnable from './runnable-and-suite' -import RunnableHeader from './runnable-header' +import Runnable, { shouldShowConnectionDots } from './runnable-and-suite' import type { RunnablesStore, RunnableArray } from './runnables-store' -import statsStore, { StatsStore } from '../header/stats-store' +import type { StatsStore } from '../header/stats-store' import type { Scroller, UserScrollCallback } from '../lib/scroller' import type { AppState } from '../lib/app-state' import OpenFileInIDE from '../lib/open-file-in-ide' @@ -48,7 +47,7 @@ const RunnablesEmptyState = ({ spec, studioEnabled, eventManager = events }: Run No tests found.

        Cypress could not detect tests in this file.

        - { !isAllSpecs && ( + {!isAllSpecs && ( <> = observer(({ runnables, studioEnabled, canSaveStudioLogs }: RunnablesListProps) => ( -
        -
          - {_.map(runnables, (runnable) => - ())} -
        -
        -)) +const RunnablesList: React.FC = observer(({ runnables, studioEnabled, canSaveStudioLogs }: RunnablesListProps) => { + return ( +
        +
          + {_.map(runnables, (runnable, index) => + ())} +
        +
        + ) +}) RunnablesList.displayName = 'RunnablesList' @@ -182,7 +184,6 @@ const Runnables: React.FC = observer(({ appState, scroller, erro return (
        - child.type === 'test') + } + + get _testChildStates () { + /** + * since we're displaying a collapsible for each suite whether it's a nested suite or not, + * we only want to consider the test children of the current suite and not the state of any suite children + */ + return _.map(this._testChildren, 'state') } get hasRetried (): boolean { - return _.some(this.children, (v) => v.hasRetried) + return _.some(this._testChildren, (v) => v.hasRetried) + } + + get _anyTestChildrenRunning () { + return _.some(this._testChildStates, (state) => { + return state === 'active' + }) } - get _anyChildrenFailed () { - return _.some(this._childStates, (state) => { + get _anyTestChildrenFailed () { + return _.some(this._testChildStates, (state) => { return state === 'failed' }) } - get _allChildrenPassedOrPending () { - return !this._childStates.length || _.every(this._childStates, (state) => { + get _allTestChildrenPassedOrPending () { + return !this._testChildStates.length || _.every(this._testChildStates, (state) => { return state === 'passed' || state === 'pending' }) } - get _allChildrenPending () { - return !!this._childStates.length - && _.every(this._childStates, (state) => { + get _allTestChildrenPending () { + return !!this._testChildStates.length + && _.every(this._testChildStates, (state) => { return state === 'pending' }) } diff --git a/packages/reporter/src/sessions/sessions.scss b/packages/reporter/src/sessions/sessions.scss index 3834c1f934a5..05da32a406b8 100644 --- a/packages/reporter/src/sessions/sessions.scss +++ b/packages/reporter/src/sessions/sessions.scss @@ -15,6 +15,10 @@ padding: 4px; cursor: pointer; + > span { + width: max-content; + } + &:hover, &:focus { color: $gray-50; diff --git a/packages/reporter/src/test/test.tsx b/packages/reporter/src/test/test.tsx index 51c53e13b03b..d41b4c1d810c 100644 --- a/packages/reporter/src/test/test.tsx +++ b/packages/reporter/src/test/test.tsx @@ -7,7 +7,6 @@ import cs from 'classnames' import events, { Events } from '../lib/events' import appState, { AppState } from '../lib/app-state' import Collapsible from '../collapsible/collapsible' -import { indent } from '../lib/util' import TestModel from './test-model' import scroller, { Scroller } from '../lib/scroller' @@ -17,7 +16,6 @@ import { LaunchStudioIcon } from '../components/LaunchStudioIcon' import CheckIcon from '@packages/frontend-shared/src/assets/icons/checkmark_x16.svg' import ClipboardIcon from '@packages/frontend-shared/src/assets/icons/general-clipboard_x16.svg' -import WarningIcon from '@packages/frontend-shared/src/assets/icons/warning_x16.svg' interface StudioControlsProps { events?: Events @@ -137,16 +135,6 @@ const Test: React.FC = observer(({ model, events: eventsProps = event const _controls = () => { let controls: Array = [] - if (model.state === 'failed') { - controls.push( - - - - - , - ) - } - if (studioEnabled && !appStateProps.studioActive && model.state !== 'pending') { controls.push( = observer(({ model, events: eventsProps = event containerRef={containerRef} header={_header()} headerClass='runnable-wrapper' - headerStyle={{ paddingLeft: indent(model.level) }} contentClass='runnable-instruments' isOpen={model.isOpen} onOpenStateChangeRequested={(isOpen: boolean) => model.setIsOpen(isOpen)} hideExpander > -
        +
        _scrollIntoView()} /> - {appStateProps.studioActive && } + {appStateProps.studioActive && }
        ) diff --git a/packages/reporter/tailwind.config.js b/packages/reporter/tailwind.config.js new file mode 100644 index 000000000000..63de504b8b2f --- /dev/null +++ b/packages/reporter/tailwind.config.js @@ -0,0 +1,27 @@ +/** @type {import('tailwindcss').Config} */ + +const { TailwindConfig, TailwindIconExtractor } = require('@cypress-design/css') +const path = require('path') + +module.exports = { + presets: [TailwindConfig()], + content: { + files: [ + './src/**/*.{js,jsx,ts,tsx}', + './index.html', + path.resolve( + __dirname, + '../../node_modules/@cypress-design/*/dist/*.js|ts', + ), + ], + extract: ['mdx', 'tsx', 'jsx', 'js', 'ts'].reduce((acc, ext) => { + acc[ext] = TailwindIconExtractor + + return acc + }, {}), + }, + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/patches/prismjs+1.27.0.dev.patch b/patches/prismjs+1.27.0.dev.patch new file mode 100644 index 000000000000..75558656b333 --- /dev/null +++ b/patches/prismjs+1.27.0.dev.patch @@ -0,0 +1,69 @@ +diff --git a/node_modules/prismjs/themes/prism.css b/node_modules/prismjs/themes/prism.css +index 5b8ed2d..8173ec6 100644 +--- a/node_modules/prismjs/themes/prism.css ++++ b/node_modules/prismjs/themes/prism.css +@@ -4,8 +4,8 @@ + * @author Lea Verou + */ + +-code[class*="language-"], +-pre[class*="language-"] { ++code.language-js, code.language-ts, ++pre.language-js, pre.language-ts { + color: black; + background: none; + text-shadow: 0 1px white; +@@ -28,39 +28,43 @@ pre[class*="language-"] { + hyphens: none; + } + +-pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, +-code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { ++pre.language-js::-moz-selection, pre.language-js ::-moz-selection, ++pre.language-ts::-moz-selection, pre.language-ts ::-moz-selection, ++code.language-js::-moz-selection, code.language-js ::-moz-selection, ++code.language-ts::-moz-selection, code.language-ts ::-moz-selection { + text-shadow: none; + background: #b3d4fc; + } + +-pre[class*="language-"]::selection, pre[class*="language-"] ::selection, +-code[class*="language-"]::selection, code[class*="language-"] ::selection { ++pre.language-js::selection, pre.language-js ::selection, ++pre.language-ts::selection, pre.language-ts ::selection, ++code.language-js::selection, code.language-js ::selection, ++code.language-ts::selection, code.language-ts ::selection { + text-shadow: none; + background: #b3d4fc; + } + + @media print { +- code[class*="language-"], +- pre[class*="language-"] { ++ code.language-js, code.language-ts, ++ pre.language-js, pre.language-ts { + text-shadow: none; + } + } + + /* Code blocks */ +-pre[class*="language-"] { ++pre.language-js, pre.language-ts { + padding: 1em; + margin: .5em 0; + overflow: auto; + } + +-:not(pre) > code[class*="language-"], +-pre[class*="language-"] { ++:not(pre) > code.language-js, :not(pre) > code.language-ts, ++pre.language-js, pre.language-ts { + background: #f5f2f0; + } + + /* Inline code */ +-:not(pre) > code[class*="language-"] { ++:not(pre) > code.language-js, :not(pre) > code.language-ts { + padding: .1em; + border-radius: .3em; + white-space: normal; diff --git a/system-tests/projects/e2e/cypress/support/util.js b/system-tests/projects/e2e/cypress/support/util.js index 1ab07814d030..c608d5337b31 100644 --- a/system-tests/projects/e2e/cypress/support/util.js +++ b/system-tests/projects/e2e/cypress/support/util.js @@ -37,7 +37,7 @@ export const verify = (title, ctx, options) => { .contains(`FAIL - ${getTitle(title, ctx)}`) .closest('.collapsible') .within(() => { - cy.contains('View stack trace').click() + cy.contains('Stack trace').click() _.each([].concat(message), (msg) => { cy.get('.runnable-err-message') diff --git a/yarn.lock b/yarn.lock index a42efdf9de53..b52bc2d772d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2653,6 +2653,11 @@ tailwindcss "^3.4.3" tailwindcss-hocus "^0.0.7" +"@cypress-design/constants-button@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@cypress-design/constants-button/-/constants-button-1.9.0.tgz#56683bb760b7eaab9857c7f7384c5ac55eafef18" + integrity sha512-4mNYfEdrUJUfC4uPwr7P3u0MFsxrffvf5CefbL1HbjwxdHSLT94PhUL70kOukkN1CkM2TwPOTffNZWFLARyQeQ== + "@cypress-design/constants-spinner@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@cypress-design/constants-spinner/-/constants-spinner-1.0.1.tgz#33904995ea95c34867905aa09391d5364ffcc129" @@ -2670,10 +2675,10 @@ dependencies: "@cypress-design/icon-registry" "^1.5.1" -"@cypress-design/css@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@cypress-design/css/-/css-1.1.0.tgz#707d6d51fb7a43be304a501fccdc08d2c134825c" - integrity sha512-xHLkTQNKIpKqcK0bVQBOTCISDBV+Z+HD8MyfQyNKuYRy408bwzICi+LsrSf0attsqEoE57Jp2n6VoNbNbPXHqg== +"@cypress-design/css@1.2.0", "@cypress-design/css@^1.1.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@cypress-design/css/-/css-1.2.0.tgz#e70488ba87d87fa3389f1d96a74d777f2cba5dec" + integrity sha512-7UBmEEzJ8EerqZdC0pfVL2ZEAkmnArFVHJxztK2YmZ/rVOh9ZKBl1A5vcuGgB8jtTsQ+XMyO7JbSTJn3OgH/DA== dependencies: "@tailwindcss/container-queries" "^0.1.1" lodash "^4.17.21" @@ -2681,13 +2686,28 @@ tailwindcss "^3.4.3" tailwindcss-hocus "^0.0.7" -"@cypress-design/icon-registry@^1.0.0", "@cypress-design/icon-registry@^1.18.0", "@cypress-design/icon-registry@^1.5.1": - version "1.18.0" - resolved "https://registry.yarnpkg.com/@cypress-design/icon-registry/-/icon-registry-1.18.0.tgz#799c5ac8f8362aebdcf2181119c3205136ca5ab9" - integrity sha512-4goChP9rWVq7F/+c36JyJ4quvHSyI6gkjJ/IKFqncNwkC3gvVeJ4GQX2mqQJCQ+z0Er+2Mmzcw7JiVo1GpbJlg== +"@cypress-design/icon-registry@^1.0.0", "@cypress-design/icon-registry@^1.18.0", "@cypress-design/icon-registry@^1.27.0", "@cypress-design/icon-registry@^1.5.1": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@cypress-design/icon-registry/-/icon-registry-1.27.0.tgz#a657acc40cc6b43e14ebf1a0e19eb65f43e08a76" + integrity sha512-2/jlv/0RsCwwZpovIk8sjXsbqnnNgmpYiaQUl9XUoZ45rurhr2PuOwYk1HMAkrBcv05adzWLfiEtfzb5TwUG1w== dependencies: "@cypress-design/color-constants" "^1.1.0" +"@cypress-design/react-button@^1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@cypress-design/react-button/-/react-button-1.10.1.tgz#31328d00789cf8a59898afdce1b71eecadec9de1" + integrity sha512-NFRWZRmYREaElTZj8ioyNu1lzaf0WperEw98EqC+LOYpwZzHtLahR0Ot/1DGOiCZTacrvBrtjo65VQGieXqJfQ== + dependencies: + clsx "^2.1.1" + +"@cypress-design/react-icon@^1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@cypress-design/react-icon/-/react-icon-1.27.0.tgz#e34952329887deb614b78b66d0357b7a4049cbe8" + integrity sha512-r8tu7JFwJWwsKSRMJ83ocSb6zfM0Hjnf2DTH5CEhlVxo/QHMOVk85zQSqxVHlX9p11C7jomUeMkoalD6dpKXTg== + dependencies: + "@cypress-design/icon-registry" "^1.27.0" + clsx "^2.1.1" + "@cypress-design/vue-button@^1.1.0", "@cypress-design/vue-button@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@cypress-design/vue-button/-/vue-button-1.6.0.tgz#e7266dfe11c31628ef3a979fffcf041b141e39c3" @@ -10478,7 +10498,7 @@ autobarrel@^1.1.0: minimist "^1.2.5" tslib "^2.0.0" -autoprefixer@10.4.20, autoprefixer@^10.4.20: +autoprefixer@10.4.20: version "10.4.20" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== @@ -10490,6 +10510,18 @@ autoprefixer@10.4.20, autoprefixer@^10.4.20: picocolors "^1.0.1" postcss-value-parser "^4.2.0" +autoprefixer@10.4.21, autoprefixer@^10.4.20: + version "10.4.21" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.21.tgz#77189468e7a8ad1d9a37fbc08efc9f480cf0a95d" + integrity sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ== + dependencies: + browserslist "^4.24.4" + caniuse-lite "^1.0.30001702" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.1.1" + postcss-value-parser "^4.2.0" + autoprefixer@^9.4.5: version "9.8.6" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" @@ -11245,15 +11277,15 @@ browserslist-to-esbuild@^2.1.1: dependencies: meow "^13.0.0" -browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0: - version "4.24.4" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" - integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== +browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.23.0, browserslist@^4.23.1, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.24.4: + version "4.25.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.25.1.tgz#ba9e8e6f298a1d86f829c9b975e07948967bb111" + integrity sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw== dependencies: - caniuse-lite "^1.0.30001688" - electron-to-chromium "^1.5.73" + caniuse-lite "^1.0.30001726" + electron-to-chromium "^1.5.173" node-releases "^2.0.19" - update-browserslist-db "^1.1.1" + update-browserslist-db "^1.1.3" bser@2.1.1: version "2.1.1" @@ -11662,10 +11694,10 @@ camelcase@^7.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== -caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001688: - version "1.0.30001704" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001704.tgz#6644fe909d924ac3a7125e8a0ab6af95b1f32990" - integrity sha512-+L2IgBbV6gXB4ETf0keSvLr7JUrRVbIaB/lrQ1+z8mRcQiisG5k+lG6O4n6Y5q6f5EuNfaYXKgymucphlEXQew== +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001702, caniuse-lite@^1.0.30001726: + version "1.0.30001726" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz#a15bd87d5a4bf01f6b6f70ae7c97fdfd28b5ae47" + integrity sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw== capital-case@^1.0.4: version "1.0.4" @@ -12399,6 +12431,11 @@ cloneable-readable@^1.0.0: process-nextick-args "^2.0.0" readable-stream "^2.3.5" +clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + cmd-shim@6.0.3, cmd-shim@^6.0.0: version "6.0.3" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.3.tgz#c491e9656594ba17ac83c4bd931590a9d6e26033" @@ -14583,10 +14620,10 @@ electron-publish@25.1.7: lazy-val "^1.0.5" mime "^2.5.2" -electron-to-chromium@^1.5.73: - version "1.5.116" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.116.tgz#b779d73cd0cc75305d12ae4f061d7f7bcee4c761" - integrity sha512-mufxTCJzLBQVvSdZzX1s5YAuXsN1M4tTyYxOOL1TcSKtIzQ9rjIrm7yFK80rN5dwGTePgdoABDSHpuVtRQh0Zw== +electron-to-chromium@^1.5.173: + version "1.5.174" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.174.tgz#c7d273755d4dc9bc4f1d192f6f2092bee42771f3" + integrity sha512-HE43yYdUUiJVjewV2A9EP8o89Kb4AqMKplMQP2IxEPUws1Etu/ZkdsgUDabUZ/WmbP4ZbvJDOcunvbBUPPIfmw== electron@33.2.1: version "33.2.1" @@ -22636,11 +22673,6 @@ mockery@2.1.0: resolved "https://registry.yarnpkg.com/mockery/-/mockery-2.1.0.tgz#5b0aef1ff564f0f8139445e165536c7909713470" integrity sha512-9VkOmxKlWXoDO/h1jDZaS4lH33aWfRiJiNT/tKj+8OGzrcFDLo8d0syGdbsc3Bc4GvRXPb+NMMvojotmuGJTvA== -modern-normalize@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7" - integrity sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA== - modify-filename@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1" @@ -22799,7 +22831,7 @@ nanoid@3.3.1: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== -nanoid@^3.3.8: +nanoid@^3.3.11: version "3.3.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== @@ -25326,6 +25358,15 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== +postcss@8.5.6, postcss@^8.1.10, postcss@^8.2.14, postcss@^8.2.7, postcss@^8.4.21, postcss@^8.4.22, postcss@^8.4.27, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.47, postcss@^8.5.3: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + postcss@^6.0.14, postcss@^6.0.9: version "6.0.23" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" @@ -25343,15 +25384,6 @@ postcss@^7.0.11, postcss@^7.0.18, postcss@^7.0.32: picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.1.10, postcss@^8.2.14, postcss@^8.2.7, postcss@^8.4.21, postcss@^8.4.22, postcss@^8.4.27, postcss@^8.4.38, postcss@^8.4.43, postcss@^8.4.47, postcss@^8.5.3: - version "8.5.3" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" - integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== - dependencies: - nanoid "^3.3.8" - picocolors "^1.1.1" - source-map-js "^1.2.1" - postject@^1.0.0-alpha.6: version "1.0.0-alpha.6" resolved "https://registry.yarnpkg.com/postject/-/postject-1.0.0-alpha.6.tgz#9d022332272e2cfce8dea4cfce1ee6dd1b2ee135" @@ -29309,6 +29341,11 @@ tailwindcss@1.1.4: pretty-hrtime "^1.0.3" reduce-css-calc "^2.1.6" +tailwindcss@4.1.10: + version "4.1.10" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-4.1.10.tgz#515741b0a79316d1971d182f7fbc435b68679373" + integrity sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA== + tailwindcss@^3.3.1, tailwindcss@^3.4.3: version "3.4.17" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63" @@ -30737,7 +30774,7 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-browserslist-db@^1.1.1: +update-browserslist-db@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420" integrity sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==