Best JavaScript code snippet using cypress
settings_spec.js
Source:settings_spec.js
1const { _ } = Cypress2const { each, flow, get, isString, join, map, merge, set, sortBy, toPairs } = require('lodash/fp')3describe('Settings', () => {4 beforeEach(function () {5 cy.fixture('user').as('user')6 cy.fixture('config').as('config')7 cy.fixture('projects').as('projects')8 cy.fixture('projects_statuses').as('projectStatuses')9 cy.fixture('specs').as('specs')10 cy.fixture('runs').as('runs')11 cy.fixture('keys').as('keys')12 this.goToSettings = () => {13 cy.get('.navbar-default')14 cy.get('a').contains('Settings').click()15 // make sure the common sections are shown16 cy.get('.settings-config')17 cy.get('.settings-proxy')18 }19 cy.visitIndex().then(function (win) {20 let start = win.App.start21 this.win = win22 this.ipc = win.App.ipc23 cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' })24 cy.stub(this.ipc, 'getCurrentUser').resolves(this.user)25 cy.stub(this.ipc, 'updaterCheck').resolves(false)26 cy.stub(this.ipc, 'getSpecs').yields(null, this.specs)27 cy.stub(this.ipc, 'closeBrowser').resolves()28 cy.stub(this.ipc, 'closeProject').resolves()29 cy.stub(this.ipc, 'pingApiServer').resolves()30 cy.stub(this.ipc, 'onConfigChanged')31 cy.stub(this.ipc, 'onFocusTests')32 cy.stub(this.ipc, 'externalOpen')33 cy.stub(this.ipc, 'setClipboardText')34 this.openProject = this.util.deferred()35 cy.stub(this.ipc, 'openProject').returns(this.openProject.promise)36 this.getProjectStatus = this.util.deferred()37 cy.stub(this.ipc, 'getProjectStatus').returns(this.getProjectStatus.promise)38 this.getRecordKeys = this.util.deferred()39 cy.stub(this.ipc, 'getRecordKeys').returns(this.getRecordKeys.promise)40 start()41 })42 })43 describe('general functionality', () => {44 beforeEach(function () {45 this.openProject.resolve(this.config)46 this.projectStatuses[0].id = this.config.projectId47 this.getProjectStatus.resolve(this.projectStatuses[0])48 this.goToSettings()49 })50 it('navigates to settings page', () => {51 cy.contains('Configuration')52 })53 it('highlight settings nav', () => {54 cy.contains('a', 'Settings').should('have.class', 'active')55 })56 it('collapses panels by default', function () {57 cy.contains('Your project\'s configuration is displayed').should('not.exist')58 cy.contains('Record Keys allow you to').should('not.exist')59 cy.contains(this.config.projectId).should('not.exist')60 cy.percySnapshot()61 })62 context('on:focus:tests clicked', () => {63 beforeEach(function () {64 this.ipc.onFocusTests.yield()65 })66 it('routes to specs page', () => {67 cy.shouldBeOnProjectSpecs()68 })69 })70 })71 /**72 * Opens "Configuration" panel of the Settings tab73 * and checks that configuration element is fully visible.74 * This helps to ensure no flake down the line75 */76 const openConfiguration = () => {77 cy.contains('Configuration').click()78 cy.get('.config-vars').should('be.visible')79 .invoke('height').should('be.gt', 400)80 }81 describe('configuration panel', () => {82 describe('displays config', () => {83 beforeEach(function () {84 this.openProject.resolve(this.config)85 this.projectStatuses[0].id = this.config.projectId86 this.getProjectStatus.resolve(this.projectStatuses[0])87 this.goToSettings()88 openConfiguration()89 })90 it('displays config section', () => {91 cy.contains('Your project\'s configuration is displayed')92 })93 it('displays browser information which is collapsed by default', () => {94 cy.contains('.config-vars', 'browsers')95 cy.get('.config-vars').invoke('text')96 .should('not.contain', '0:Chrome')97 cy.contains('span', 'browsers').parents('div').first().find('span').first().click()98 cy.get('.config-vars').invoke('text')99 .should('contain', '0:Chrome')100 cy.ensureAnimationsFinished()101 cy.percySnapshot()102 })103 it('removes the summary list of values once a key is expanded', () => {104 cy.contains('span', 'browsers').parents('div').first().find('span').first().click()105 cy.get('.config-vars').invoke('text')106 .should('not.contain', 'Chrome, Chromium')107 cy.get('.config-vars').invoke('text')108 .should('contain', '0:Chrome')109 })110 it('distinguishes between Arrays and Objects when expanded', () => {111 cy.get('.config-vars').invoke('text')112 .should('not.contain', 'browsers: Array (4)')113 cy.contains('span', 'browsers').parents('div').first().find('span').first().click()114 cy.get('.config-vars').invoke('text')115 .should('contain', 'browsers: Array (4)')116 })117 it('applies the same color treatment to expanded key values as the root key', () => {118 cy.contains('span', 'browsers').parents('div').first().find('span').first().click()119 cy.get('.config-vars').as('config-vars')120 .contains('span', 'Chrome').parent('span').should('have.class', 'plugin')121 cy.get('@config-vars')122 .contains('span', 'Chromium').parent('span').should('have.class', 'plugin')123 cy.get('@config-vars')124 .contains('span', 'Canary').parent('span').should('have.class', 'plugin')125 cy.get('@config-vars')126 .contains('span', 'Electron').parent('span').should('have.class', 'plugin')127 cy.contains('span', 'blockHosts').parents('div').first().find('span').first().click()128 cy.get('@config-vars')129 .contains('span', 'www.google-analytics.com').parent('span').should('have.class', 'config')130 cy.get('@config-vars')131 .contains('span', 'hotjar.com').parent('span').should('have.class', 'config')132 cy.contains('span', 'hosts').parents('div').first().find('span').first().click()133 cy.get('@config-vars')134 .contains('span', '127.0.0.1').parent('span').should('have.class', 'config')135 cy.get('@config-vars')136 .contains('span', '127.0.0.2').parent('span').should('have.class', 'config')137 cy.get('@config-vars')138 .contains('span', 'Electron').parents('div').first().find('span').first().click()139 cy.get('@config-vars').contains('span', 'electron').parents('li').eq(1).find('.line .plugin').should('have.length', 6)140 })141 it('displays string values as quoted strings', () => {142 cy.get('.config-vars').invoke('text')143 .should('contain', 'baseUrl:"http://localhost:8080"')144 })145 it('displays undefined and null without quotations', () => {146 cy.get('.config-vars').invoke('text')147 .should('not.contain', '"undefined"')148 .should('not.contain', '"null"')149 })150 it('does not show the root config label', () => {151 cy.get('.config-vars').find('> ol > li > div').should('have.css', 'display', 'none')152 })153 it('displays legend in table', () => {154 cy.get('table>tbody>tr').should('have.length', 6)155 })156 it('displays "true" values', () => {157 cy.get('.line').contains('true')158 })159 it('displays "null" values', () => {160 cy.get('.line').contains('null')161 })162 it('displays "object" values for env and hosts', () => {163 cy.get('.line').contains('www.google-analytics.com, hotjar.com')164 cy.get('.line').contains('*.foobar.com, *.bazqux.com')165 })166 it('displays "array" values for blockHosts', () => {167 cy.contains('.line', 'blockHosts').contains('www.google-analytics.com, hotjar.com')168 })169 it('opens help link on click', () => {170 cy.get('.settings-config .learn-more').click().then(function () {171 expect(this.ipc.externalOpen).to.be.calledWithMatch({ url: 'https://on.cypress.io/guides/configuration' })172 })173 })174 it('displays null when env settings are empty or not defined', function () {175 this.ipc.openProject.resolves(setConfigEnv(this.config, undefined))176 this.ipc.onConfigChanged.yield()177 cy.contains('.line', 'env:null').then(() => {178 this.ipc.openProject.resolves(this.config)179 this.ipc.onConfigChanged.yield()180 cy.contains('.line', 'env:fileServerFolder')181 .then(() => {182 this.ipc.openProject.resolves(setConfigEnv(this.config, null))183 this.ipc.onConfigChanged.yield()184 cy.contains('.line', 'env:null').then(() => {185 this.ipc.openProject.resolves(this.config)186 this.ipc.onConfigChanged.yield()187 cy.contains('.line', 'env:fileServerFolder')188 .then(() => {189 this.ipc.openProject.resolves(setConfigEnv(this.config, {}))190 this.ipc.onConfigChanged.yield()191 cy.contains('.line', 'env:null')192 })193 })194 })195 })196 })197 it('displays env settings', () => {198 cy.get('@config').then(({ resolved }) => {199 const getEnvKeys = flow([200 get('env'),201 toPairs,202 map(([key]) => key),203 sortBy(get('')),204 ])205 const assertKeyExists = each((key) => cy.contains('.line', key))206 const assertKeyValuesExists = flow([207 map((key) => {208 return flow([209 get(['env', key, 'value']),210 (v) => {211 if (isString(v)) {212 return `"${v}"`213 }214 return v215 },216 ])(resolved)217 }),218 each((v) => {219 cy.contains('.key-value-pair-value', v)220 }),221 ])222 const assertFromTooltipsExist = flow([223 map((key) => {224 return [key,225 flow([226 get(['env', key, 'from']),227 (from) => `.${from}`,228 ])(resolved)]229 }),230 each(([key, fromTooltipClassName]) => {231 cy.contains(key).parents('.line').first().find(fromTooltipClassName)232 }),233 ])234 cy.contains('.line', 'env').contains(flow([getEnvKeys, join(', ')])(resolved))235 cy.contains('.line', 'env').click()236 flow([getEnvKeys, assertKeyExists])(resolved)237 flow([getEnvKeys, assertKeyValuesExists])(resolved)238 flow([getEnvKeys, assertFromTooltipsExist])(resolved)239 })240 })241 })242 context('on config changes', () => {243 beforeEach(function () {244 this.projectStatuses[0].id = this.config.projectId245 this.getProjectStatus.resolve(this.projectStatuses[0])246 const newConfig = this.util.deepClone(this.config)247 newConfig.clientUrl = 'http://localhost:8888'248 newConfig.clientUrlDisplay = 'http://localhost:8888'249 newConfig.browsers = this.browsers250 this.openProject.resolve(newConfig)251 this.goToSettings()252 openConfiguration()253 })254 it('displays updated config', function () {255 const newConfig = this.util.deepClone(this.config)256 newConfig.resolved.baseUrl.value = 'http://localhost:7777'257 this.ipc.openProject.onCall(1).resolves(newConfig)258 this.ipc.onConfigChanged.yield()259 cy.contains('http://localhost:7777')260 })261 })262 context('when configFile is false', () => {263 beforeEach(function () {264 this.openProject.resolve(Cypress._.assign({265 configFile: false,266 }, this.config))267 this.goToSettings()268 openConfiguration()269 })270 it('notes that cypress.json is disabled', () => {271 cy.contains('set from cypress.json file (currently disabled by --config-file false)')272 })273 })274 context('when configFile is set', function () {275 beforeEach(function () {276 this.openProject.resolve(Cypress._.assign({277 configFile: 'special-cypress.json',278 }, this.config))279 this.goToSettings()280 openConfiguration()281 })282 it('notes that a custom config is in use', () => {283 cy.contains('set from custom config file special-cypress.json')284 })285 })286 })287 describe('project id panel', () => {288 beforeEach(function () {289 this.openProject.resolve(this.config)290 this.projectStatuses[0].id = this.config.projectId291 this.getProjectStatus.resolve(this.projectStatuses[0])292 this.goToSettings()293 cy.contains('Project ID').click()294 })295 it('displays project id section', function () {296 cy.contains(this.config.projectId)297 cy.percySnapshot()298 })299 it('shows tooltip on hover of copy to clipboard', () => {300 cy.get('.action-copy').trigger('mouseover')301 cy.get('.cy-tooltip').should('contain', 'Copy to clipboard')302 })303 it('copies project id config to clipboard', function () {304 cy.get('.action-copy').click()305 .then(() => {306 const expectedJsonConfig = {307 projectId: this.config.projectId,308 }309 const expectedCopyCommand = JSON.stringify(expectedJsonConfig, null, 2)310 expect(this.ipc.setClipboardText).to.be.calledWith(expectedCopyCommand)311 })312 })313 })314 describe('record key panel', () => {315 context('when project is set up and you have access', () => {316 beforeEach(function () {317 this.openProject.resolve(this.config)318 this.projectStatuses[0].id = this.config.projectId319 this.getProjectStatus.resolve(this.projectStatuses[0])320 this.goToSettings()321 cy.contains('Record Key').click()322 })323 it('displays record key section', () => {324 cy.contains('A Record Key sends')325 })326 it('opens ci guide when learn more is clicked', () => {327 cy.get('.settings-record-key').contains('Learn more').click().then(function () {328 expect(this.ipc.externalOpen).to.be.calledWithMatch({ url: 'https://on.cypress.io/what-is-a-record-key' })329 })330 })331 it('loads the projects record key', function () {332 expect(this.ipc.getRecordKeys).to.be.called333 })334 it('shows spinner', () => {335 cy.get('.settings-record-key .fa-spinner')336 })337 describe('when record key loads', () => {338 beforeEach(function () {339 this.getRecordKeys.resolve(this.keys)340 })341 it('displays first Record Key', function () {342 cy.get('.loading-record-keys').should('not.exist')343 cy.get('.settings-record-key')344 .contains(`cypress run --record --key ${this.keys[0].id}`)345 cy.percySnapshot()346 })347 it('shows tooltip on hover of copy to clipboard', () => {348 cy.get('.settings-record-key').find('.action-copy').trigger('mouseover')349 cy.get('.cy-tooltip').should('contain', 'Copy to clipboard')350 })351 it('copies record key command to clipboard', function () {352 cy.get('.settings-record-key').find('.action-copy').click()353 .then(() => {354 expect(this.ipc.setClipboardText).to.be.calledWith(`cypress run --record --key ${this.keys[0].id}`)355 })356 })357 it('opens admin project settings when record key link is clicked', () => {358 cy.get('.settings-record-key').contains('You can change').click().then(function () {359 expect(this.ipc.externalOpen).to.be.calledWith(`https://on.cypress.io/dashboard/projects/${this.config.projectId}/settings`)360 })361 })362 })363 describe('when there are no keys', () => {364 beforeEach(function () {365 this.getRecordKeys.resolve([])366 })367 it('displays empty message', () => {368 cy.get('.settings-record-key .empty-well').should('contain', 'This project has no record keys')369 cy.percySnapshot()370 })371 it('opens dashboard project settings when clicking \'Dashboard\'', () => {372 cy.get('.settings-record-key .empty-well a').click().then(function () {373 expect(this.ipc.externalOpen).to.be.calledWith(`https://on.cypress.io/dashboard/projects/${this.config.projectId}/settings`)374 })375 })376 })377 describe('when the user is logged out', () => {378 beforeEach(function () {379 this.getRecordKeys.resolve([])380 cy.logOut()381 })382 it('shows message that user must be logged in to view record keys', () => {383 cy.get('.empty-well').should('contain', 'must be logged in')384 cy.ensureAnimationsFinished()385 cy.percySnapshot()386 })387 it('opens login modal after clicking \'Log In\'', () => {388 cy.get('.empty-well button').click()389 cy.get('.login')390 })391 it('re-loads and shows the record key when user logs in', function () {392 cy.stub(this.ipc, 'beginAuth').resolves(this.user)393 this.ipc.getRecordKeys.onCall(1).resolves(this.keys)394 cy.get('.empty-well button').click()395 cy.contains('Log In to Dashboard').click().should(() => {396 expect(this.ipc.getRecordKeys).to.be.calledTwice397 })398 cy.get('.settings-record-key')399 .contains(`cypress run --record --key ${this.keys[0].id}`)400 // extra insurance that panel in background is fully expanded401 cy.contains('You can change this key')402 cy.ensureAnimationsFinished()403 cy.percySnapshot()404 })405 })406 })407 context('when project is not set up for CI', () => {408 it('does not show ci Keys section when project has no id', function () {409 const newConfig = this.util.deepClone(this.config)410 newConfig.projectId = null411 this.openProject.resolve(newConfig)412 this.getProjectStatus.resolve(this.projectStatuses)413 this.goToSettings()414 cy.contains('h5', 'Record Keys').should('not.exist')415 cy.percySnapshot()416 })417 it('does not show ci Keys section when project is invalid', function () {418 this.openProject.resolve(this.config)419 this.projectStatuses[0].state = 'INVALID'420 this.getProjectStatus.resolve(this.projectStatuses[0])421 this.goToSettings()422 cy.contains('h5', 'Record Keys').should('not.exist')423 cy.percySnapshot()424 })425 })426 context('when you are not a user of this projects org', () => {427 beforeEach(function () {428 this.openProject.resolve(this.config)429 })430 it('does not show record key', function () {431 this.projectStatuses[0].state = 'UNAUTHORIZED'432 this.getProjectStatus.resolve(this.projectStatuses[0])433 this.goToSettings()434 cy.contains('h5', 'Record Keys').should('not.exist')435 })436 })437 })438 describe('node version panel', () => {439 const bundledNodeVersion = '1.2.3'440 const systemNodePath = '/foo/bar/node'441 const systemNodeVersion = '4.5.6'442 beforeEach(function () {443 this.navigateWithConfig = function (config) {444 this.openProject.resolve(_.defaults(config, this.config))445 this.projectStatuses[0].id = this.config.projectId446 this.getProjectStatus.resolve(this.projectStatuses[0])447 this.goToSettings()448 }449 })450 it('with bundled node informs user we\'re using bundled node', function () {451 this.navigateWithConfig({})452 cy.contains(`Node.js Version (${bundledNodeVersion})`).click()453 cy.get('.node-version')454 .should('contain', 'bundled with Cypress')455 .should('not.contain', systemNodePath)456 .should('not.contain', systemNodeVersion)457 cy.percySnapshot()458 })459 it('with custom node displays path to custom node', function () {460 this.navigateWithConfig({461 resolvedNodePath: systemNodePath,462 resolvedNodeVersion: systemNodeVersion,463 })464 cy.contains(`Node.js Version (${systemNodeVersion})`).click()465 cy.get('.node-version')466 .should('contain', systemNodePath)467 .should('contain', systemNodeVersion)468 .should('not.contain', bundledNodeVersion)469 })470 })471 describe('proxy settings panel', () => {472 beforeEach(function () {473 this.openProject.resolve(this.config)474 this.config.resolved.baseUrl.value = 'http://localhost:7777'475 this.projectStatuses[0].id = this.config.projectId476 this.getProjectStatus.resolve(this.projectStatuses[0])477 this.goToSettings()478 cy.contains('Proxy Settings').click()479 })480 it('with no proxy config set informs the user no proxy configuration is active', () => {481 cy.get('.settings-proxy').should('contain', 'There is no active proxy configuration.')482 })483 it('opens help link on click', () => {484 cy.get('.settings-proxy .learn-more').click().then(function () {485 expect(this.ipc.externalOpen).to.be.calledWithMatch({ url: 'https://on.cypress.io/proxy-configuration' })486 })487 })488 it('with Windows proxy settings indicates proxy and the source', () => {489 cy.setAppStore({490 projectRoot: '/foo/bar',491 proxySource: 'win32',492 proxyServer: 'http://foo-bar.baz',493 proxyBypassList: 'a,b,c,d',494 })495 cy.get('.settings-proxy').should('contain', 'from Windows system settings')496 cy.get('.settings-proxy tr:nth-child(1) > td > code').should('contain', 'http://foo-bar.baz')497 cy.get('.settings-proxy tr:nth-child(2) > td > code').should('contain', 'a, b, c, d')498 cy.percySnapshot()499 })500 it('with environment proxy settings indicates proxy and the source', () => {501 cy.setAppStore({502 projectRoot: '/foo/bar',503 proxyServer: 'http://foo-bar.baz',504 proxyBypassList: 'a,b,c,d',505 })506 })507 it('with no bypass list but a proxy set shows \'none\' in bypass list', () => {508 cy.setAppStore({509 projectRoot: '/foo/bar',510 proxyServer: 'http://foo-bar.baz',511 })512 cy.get('.settings-proxy tr:nth-child(2) > td').should('contain', 'none')513 })514 })515 describe('experiments panel', () => {516 const hasNoExperimentsPanel = () => {517 // there are several settings panels,518 // let's make sure they are loaded519 cy.get('[class*=settings-]').should('have.length.gt', 1)520 // but the experiments panel should not be there at all521 cy.get('.settings-experiments').should('not.exist')522 }523 describe('no experimental features turned on', () => {524 beforeEach(function () {525 this.openProject.resolve(this.config)526 this.projectStatuses[0].id = this.config.projectId527 this.getProjectStatus.resolve(this.projectStatuses[0])528 this.goToSettings()529 })530 it('displays panel with no experiments', () => {531 hasNoExperimentsPanel()532 cy.percySnapshot()533 })534 })535 describe('unknown experiments', () => {536 beforeEach(function () {537 this.config.experimentalFoo = true538 this.config.resolved.experimentalFoo = {539 value: true,540 }541 this.openProject.resolve(this.config)542 this.projectStatuses[0].id = this.config.projectId543 this.getProjectStatus.resolve(this.projectStatuses[0])544 this.goToSettings()545 })546 it('are not shown', () => {547 hasNoExperimentsPanel()548 })549 })550 describe('experimental feature exists', () => {551 beforeEach(function () {552 // do not overwrite the shared object reference -553 // because it is used by the app's code.554 this.win.experimental.names.experimentalCoolFeature = 'Cool Feature'555 this.win.experimental.summaries.experimentalCoolFeature = 'Enables super cool feature from Cypress where you can see the cool feature'556 })557 const hasLearnMoreLink = () => {558 cy.get('[data-cy=experiments]').contains('a', 'Learn more').click()559 .then(function () {560 expect(this.ipc.externalOpen).to.be.calledWithMatch({ url: 'https://on.cypress.io/experiments' })561 })562 }563 context('enabled', () => {564 beforeEach(function () {565 this.config.experimentalCoolFeature = true566 this.config.resolved.experimentalCoolFeature = {567 value: true,568 }569 this.openProject.resolve(this.config)570 this.projectStatuses[0].id = this.config.projectId571 this.getProjectStatus.resolve(this.projectStatuses[0])572 this.goToSettings()573 cy.contains('Experiments').click()574 })575 it('has learn more link', hasLearnMoreLink)576 it('displays experiment', () => {577 cy.get('.settings-experiments').contains('Cool Feature')578 cy.get('.experiment-status-sign')579 .should('have.class', 'enabled')580 .and('have.text', 'enabled')581 cy.percySnapshot()582 })583 })584 context('disabled', () => {585 beforeEach(function () {586 this.config.experimentalCoolFeature = false587 this.config.resolved.experimentalCoolFeature = {588 value: false,589 from: 'default',590 }591 this.openProject.resolve(this.config)592 this.projectStatuses[0].id = this.config.projectId593 this.getProjectStatus.resolve(this.projectStatuses[0])594 this.goToSettings()595 cy.contains('Experiments').click()596 })597 it('displays experiment', () => {598 cy.get('.settings-experiments').contains('Cool Feature')599 cy.get('.experiment-status-sign')600 .should('have.class', 'disabled')601 .and('have.text', 'disabled')602 cy.percySnapshot()603 })604 })605 })606 })607 describe('file preference panel', () => {608 const availableEditors = [609 { id: 'atom', name: 'Atom', isOther: false, openerId: 'atom' },610 { id: 'vim', name: 'Vim', isOther: false, openerId: 'vim' },611 { id: 'sublime', name: 'Sublime Text', isOther: false, openerId: 'sublime' },612 { id: 'vscode', name: 'Visual Studio Code', isOther: false, openerId: 'vscode' },613 { id: 'other', name: 'Other', isOther: true, openerId: '' },614 ]615 beforeEach(function () {616 this.getUserEditor = this.util.deferred()617 cy.stub(this.ipc, 'getUserEditor').returns(this.getUserEditor.promise)618 cy.stub(this.ipc, 'setUserEditor').resolves()619 this.openProject.resolve(this.config)620 this.projectStatuses[0].id = this.config.projectId621 this.getProjectStatus.resolve(this.projectStatuses[0])622 this.goToSettings()623 cy.contains('File Opener Preference').click()624 })625 it('displays file preference section', () => {626 cy.contains('Your preference is used to open files')627 })628 it('opens file preference guide when learn more is clicked', () => {629 cy.get('.file-preference').contains('Learn more').click().then(function () {630 expect(this.ipc.externalOpen).to.be.calledWithMatch({ url: 'https://on.cypress.io/file-opener-preference' })631 })632 })633 it('loads preferred editor and available editors', function () {634 expect(this.ipc.getUserEditor).to.be.called635 })636 it('shows spinner', () => {637 cy.get('.loading-editors')638 })639 describe('when editors load with preferred editor', () => {640 beforeEach(function () {641 this.getUserEditor.resolve({ availableEditors, preferredOpener: availableEditors[3] })642 })643 it('displays available editors with preferred one selected', () => {644 cy.get('.loading-editors').should('not.exist')645 cy.contains('Atom')646 cy.contains('Other')647 cy.contains('Visual Studio Code').closest('li').should('have.class', 'is-selected')648 })649 it('sets editor through ipc when a different editor is selected', function () {650 cy.contains('Atom').click()651 .closest('li').should('have.class', 'is-selected')652 cy.wrap(this.ipc.setUserEditor).should('be.calledWith', availableEditors[0])653 cy.percySnapshot()654 })655 })656 describe('when editors load without preferred editor', () => {657 beforeEach(function () {658 this.getUserEditor.resolve({ availableEditors })659 })660 it('does not select an editor', () => {661 cy.get('.loading-editors').should('not.exist')662 cy.get('.editor-picker li').should('not.have.class', 'is-selected')663 })664 })665 })666 describe('errors', () => {667 const errorText = 'An unexpected error occurred'668 beforeEach(function () {669 this.err = {670 message: 'Port \'2020\' is already in use.',671 name: 'Error',672 port: 2020,673 portInUse: true,674 stack: '[object Object]âµ at Object.API.get (/Users/jennifer/Dev/Projects/cypress-app/lib/errors.coffee:55:15)âµ at Object.wrapper [as get] (/Users/jennifer/Dev/Projects/cypress-app/node_modules/lodash/lodash.js:4414:19)âµ at Server.portInUseErr (/Users/jennifer/Dev/Projects/cypress-app/lib/server.coffee:58:16)âµ at Server.onError (/Users/jennifer/Dev/Projects/cypress-app/lib/server.coffee:86:19)âµ at Server.g (events.js:273:16)âµ at emitOne (events.js:90:13)âµ at Server.emit (events.js:182:7)âµ at emitErrorNT (net.js:1253:8)âµ at _combinedTickCallback (internal/process/next_tick.js:74:11)âµ at process._tickDomainCallback (internal/process/next_tick.js:122:9)âµFrom previous event:âµ at fn (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:57919:14)âµ at Object.appIpc [as ipc] (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:57939:10)âµ at openProject (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:59135:24)âµ at new Project (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:58848:34)âµ at ReactCompositeComponentMixin._constructComponentWithoutOwner (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:44052:27)âµ at ReactCompositeComponentMixin._constructComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:44034:21)âµ at ReactCompositeComponentMixin.mountComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:43953:21)âµ at Object.ReactReconciler.mountComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:51315:35)âµ at ReactCompositeComponentMixin.performInitialMount (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:44129:34)âµ at ReactCompositeComponentMixin.mountComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:44016:21)âµ at Object.ReactReconciler.mountComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:51315:35)âµ at ReactDOMComponent.ReactMultiChild.Mixin._mountChildAtIndex (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:50247:40)âµ at ReactDOMComponent.ReactMultiChild.Mixin._updateChildren (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:50163:43)âµ at ReactDOMComponent.ReactMultiChild.Mixin.updateChildren (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:50123:12)âµ at ReactDOMComponent.Mixin._updateDOMChildren (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:45742:12)âµ at ReactDOMComponent.Mixin.updateComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:45571:10)âµ at ReactDOMComponent.Mixin.receiveComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:45527:10)âµ at Object.ReactReconciler.receiveComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:51396:22)âµ at ReactCompositeComponentMixin._updateRenderedComponent (file:///Users/jennifer/Dev/Projects/cypress-core-desktop-gui/dist/app.js:44547:23)',675 type: 'PORT_IN_USE_SHORT',676 }677 this.config.resolved.baseUrl.value = 'http://localhost:7777'678 this.projectStatuses[0].id = this.config.projectId679 this.getProjectStatus.resolve(this.projectStatuses[0])680 this.openProject.resolve(this.config)681 this.goToSettings()682 openConfiguration()683 cy.contains('http://localhost:7777').then(() => {684 this.ipc.openProject.onCall(1).rejects(this.err)685 this.ipc.onConfigChanged.yield()686 })687 })688 it('displays errors', () => {689 cy.contains(errorText)690 cy.percySnapshot()691 })692 it('displays config after error is fixed', function () {693 cy.contains(errorText).then(() => {694 this.ipc.openProject.onCall(1).resolves(this.config)695 this.ipc.onConfigChanged.yield()696 })697 cy.contains('Configuration')698 })699 })700})701// --702function setConfigEnv (config, v) {703 return flow([704 merge(config),705 set('resolved.env', v),706 ])({})...
testStatus.js
Source:testStatus.js
...39 defect: [{ status: constants.STATUSOK }, { status: constants.STATUSOK }],40 effort: [{ status: constants.STATUSOK }, { status: constants.STATUSOK }],41 }42 }43 function getProjectStatus() {44 return dataStore.getDocumentByName(utils.dbCorePath(), constants.PROJECTCOLLECTION, SAMPLEPROJECTDATA.name)45 .then(project => {46 return project.status;47 });48 }49 beforeEach(() => {50 return CO(function* () {51 yield dataStore.insertData(utils.dbCorePath(), constants.PROJECTCOLLECTION, [SAMPLEPROJECTDATA]);52 });53 });54 55 it('does nothing if no statuses returned', () => {56 return CO(function* () {57 const statuses = {};58 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);59 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));60 const status = yield getProjectStatus();61 Should(status).be.undefined();62 });63 });64 it('marks the status as OK if all of the statuses are green', () => {65 return CO(function* () {66 const statuses = makeStatuses();67 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);68 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));69 const status = yield getProjectStatus();70 Should(status).equal(constants.STATUSOK);71 });72 });73 it('marks the status as WARN if any demand statuses are WARN', () => {74 return CO(function* () {75 const statuses = makeStatuses();76 statuses.demand[0].status = constants.STATUSWARNING;77 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);78 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));79 const status = yield getProjectStatus();80 Should(status).equal(constants.STATUSWARNING);81 });82 });83 it('marks the status as WARN if any defect statuses are WARN', () => {84 return CO(function* () {85 const statuses = makeStatuses();86 statuses.defect[0].status = constants.STATUSWARNING;87 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);88 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));89 const status = yield getProjectStatus();90 Should(status).equal(constants.STATUSWARNING);91 });92 });93 it('marks the status as WARN if any effort statuses are WARN', () => {94 return CO(function* () {95 const statuses = makeStatuses();96 statuses.effort[0].status = constants.STATUSWARNING;97 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);98 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));99 const status = yield getProjectStatus();100 Should(status).equal(constants.STATUSWARNING);101 });102 });103 it('marks the status as ERROR if any demand statuses are ERROR', () => {104 return CO(function* () {105 const statuses = makeStatuses();106 statuses.effort[0].status = constants.STATUSERROR;107 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);108 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));109 const status = yield getProjectStatus();110 Should(status).equal(constants.STATUSERROR);111 });112 });113 it('marks the status as ERROR if any defect statuses are ERROR', () => {114 return CO(function* () {115 const statuses = makeStatuses();116 statuses.effort[0].status = constants.STATUSERROR;117 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);118 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));119 const status = yield getProjectStatus();120 Should(status).equal(constants.STATUSERROR);121 });122 });123 it('marks the status as ERROR if any demand the statuses are ERROR', () => {124 return CO(function* () {125 const statuses = makeStatuses();126 statuses.effort[0].status = constants.STATUSERROR;127 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);128 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));129 const status = yield getProjectStatus();130 Should(status).equal(constants.STATUSERROR);131 });132 });133 it('ERROR statuses take precedence over WARN statuses', () => {134 return CO(function* () {135 const statuses = makeStatuses();136 statuses.demand[0].status = constants.STATUSERROR;137 statuses.defect[0].status = constants.STATUSWARN;138 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);139 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));140 const status = yield getProjectStatus();141 Should(status).equal(constants.STATUSERROR);142 });143 });144 it('stores the statuses in the correct collection', () => {145 return CO(function* () {146 const statuses = makeStatuses();147 sandbox.stub(statusIndicators, 'getStatuses').resolves(statuses);148 yield projectStatus.updateProjectStatus(SAMPLEPROJECTDATA, utils.dbProjectPath(SAMPLEPROJECTDATA.name));149 const status = yield dataStore.getAllData(utils.dbProjectPath(SAMPLEPROJECTDATA.name), constants.STATUSCOLLECTION)150 Should(status).match(R.flatten(makeStatuses()));151 });152 });153 afterEach(() => {154 return CO(function* () {...
model.spec.js
Source:model.spec.js
...57 dueAt: 0,58 finishedAt: 0,59 }60 const date = moment('2016-09-10')61 const status = getProjectStatus(project, date)62 expect(status).toEqual('')63 })64 it('returns empty string if date is before starting date', () => {65 const project = {66 startedAt: moment(startingDate),67 dueAt: moment(dueAtDate),68 finishedAt: moment(finishedAtDate),69 }70 const date = moment(startingDate).subtract(1, 'week')71 const status = getProjectStatus(project, date)72 expect(status).toEqual('')73 })74 it('returns empty string if date is after ending date', () => {75 const project = {76 startedAt: moment(startingDate),77 dueAt: moment(dueAtDate),78 finishedAt: moment(finishedAtDate),79 }80 const date = moment(finishedAtDate).add(1, 'week')81 const status = getProjectStatus(project, date)82 expect(status).toEqual('')83 })84 it('returns "in-progress" if date is between starting date and end date', () => {85 const project = {86 startedAt: moment(startingDate),87 dueAt: moment(dueAtDate),88 finishedAt: moment(finishedAtDate),89 }90 const date = moment('2016-09-10')91 const status = getProjectStatus(project, date)92 expect(status).toEqual('in-progress')93 })94 it('returns "in-progress delayed" if date is between finish date and due date', () => {95 const project = {96 startedAt: moment(startingDate),97 dueAt: moment(dueAtDate),98 finishedAt: moment(finishedAtDate),99 }100 const date = moment('2016-09-20')101 const status = getProjectStatus(project, date)102 expect(status).toEqual('in-progress delayed')103 })...
project.js
Source:project.js
...7 },8 mutations: {9 SET_PROJECTS (state, projects) {10 state.projects = projects.map(project => {11 const status = Vue.prototype.$getProjectStatus(project)12 const labelPhase = Vue.prototype.$getProjectLabelPhase(status, project)13 return { ...project, status, labelPhase }14 })15 },16 SET_SELECTED_PROJECT_ID (state, selectedProjectId) {17 state.selectedProjectId = selectedProjectId18 },19 CLEAR_PROJECTS (state) {20 state.projects = []21 state.selectedProjectId = 022 },23 UPDATE_PROJECT (state, project) {24 const status = Vue.prototype.$getProjectStatus(project)25 const labelPhase = Vue.prototype.$getProjectLabelPhase(status, project)26 const index = state.projects.findIndex(item => item.id === project.id)27 Vue.set(state.projects, index, { ...project, status, labelPhase })28 },29 ADD_PROJECT (state, project) {30 const status = Vue.prototype.$getProjectStatus(project)31 const labelPhase = Vue.prototype.$getProjectLabelPhase(status, project)32 state.projects.unshift({ ...project, status, labelPhase })33 }34 },35 actions: {36 loadProjects ({ commit }) {37 commit('SHOW_LOADING')38 return new Promise((resolve, reject) => {39 ProjectService40 .getProjects()41 .then(projects => {42 commit('HIDE_LOADING')43 commit('SET_PROJECTS', projects)44 resolve()...
metricsSrvc.js
Source:metricsSrvc.js
1'use strict';2app.factory("metricsSrvc", function ($log, $http) {3 var FACTORY_NAME = 'metricsSrvc';4// --------------------------------------------------------5 // - get the project's metrics6 // --------------------------------------------------------7 var getProjectMetrics = function (project_id) {8 return $http(9 {10 method: "GET",11 url: '/taskflow/apis/v1/metrics/projects/' + project_id12 }13 ).then(function (response) {14 return {15 labels: response.data.data[0],16 PV: response.data.data[1],17 EV: response.data.data[2],18 CPI: response.data.data[3],19 SPI: response.data.data[4],20 indicators: response.data.data[5]21 };22 });23 };24 // --------------------------------------------------------25 // - get the employee's metrics26 // --------------------------------------------------------27 var getEmployeeMetrics = function (employee_id, start_date, end_date) {28 return $http(29 {30 method: "POST",31 url: '/taskflow/apis/v1/metrics/employees/',32 data: {33 id: employee_id,34 start_date: start_date,35 end_date: end_date36 }37 }38 ).then(function (response) {39 return {40 labels: response.data.data[0],41 PV: response.data.data[1],42 EV: response.data.data[2]43 };44 });45 };46// --------------------------------------------------------47 // - get the project's status48 // --------------------------------------------------------49 var getProjectStatus = function (project_id) {50 return $http(51 {52 method: "GET",53 url: '/taskflow/apis/v1/metrics/status/' + project_id54 }55 ).then(function (response) {56 return {57 labels: response.data.data[0],58 HP: response.data.data[1],59 HC: response.data.data[2],60 PP: response.data.data[3],61 PC: response.data.data[4]62 };63 });64 };65 // --------------------------------------------------------66 // - return functions67 // --------------------------------------------------------68 return {69 getProjectMetrics: getProjectMetrics,70 getEmployeeMetrics: getEmployeeMetrics,71 getProjectStatus: getProjectStatus72 };73 // ==================================================...
project_spec.js
Source:project_spec.js
1describe('Project', function () {2 beforeEach(function () {3 cy.fixture('user').as('user')4 cy.fixture('config').as('config')5 cy.fixture('specs').as('specs')6 cy.fixture('projects_statuses').as('projectStatuses')7 cy.visitIndex().then((win) => {8 ({ start: this.start, ipc: this.ipc } = win.App)9 cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' })10 cy.stub(this.ipc, 'getCurrentUser').resolves(this.user)11 cy.stub(this.ipc, 'openProject').resolves(this.config)12 cy.stub(this.ipc, 'getSpecs').yields(null, this.specs)13 cy.stub(this.ipc, 'closeProject').resolves()14 cy.stub(this.ipc, 'onConfigChanged')15 cy.stub(this.ipc, 'onProjectWarning')16 this.getProjectStatus = this.util.deferred()17 cy.stub(this.ipc, 'getProjectStatus').returns(this.getProjectStatus.promise)18 this.updaterCheck = this.util.deferred()19 cy.stub(this.ipc, 'updaterCheck').resolves(this.updaterCheck.promise)20 })21 })22 describe('general behavior', function () {23 beforeEach(function () {24 this.start()25 })26 it('shows project specs', () => {27 cy.shouldBeOnProjectSpecs()28 })29 it('opens project', function () {30 cy.shouldBeOnProjectSpecs().then(() => {31 expect(this.ipc.openProject).to.be.calledWith('/foo/bar')32 })33 })34 it('gets project status', function () {35 cy.shouldBeOnProjectSpecs().then(() => {36 expect(this.ipc.getProjectStatus).to.be.calledWith({ id: this.config.projectId, path: '/foo/bar' })37 })38 })39 it('logs out user when getting project status returns 401', function () {40 cy.shouldBeOnProjectSpecs().then(() => {41 this.getProjectStatus.reject({ name: '', message: '', statusCode: 401 })42 })43 cy.shouldBeLoggedOut()44 })45 it('re-opens project if config changes', function () {46 cy.shouldBeOnProjectSpecs().then(() => {47 this.ipc.onConfigChanged.yield()48 expect(this.ipc.closeProject).to.be.called49 expect(this.ipc.openProject).to.be.called50 cy.shouldBeOnProjectSpecs()51 })52 })53 })54 describe('polling', function () {55 beforeEach(function () {56 this.ipc.getProjectStatus.resolves(this.projectStatuses[0])57 cy.clock().then(() => {58 this.start()59 })60 })61 it('gets project status every 10 seconds', function () {62 cy.tick(10000).then(() => {63 expect(this.ipc.getProjectStatus).to.be.calledTwice64 })65 })66 })...
ProjectScreen.js
Source:ProjectScreen.js
1import React from 'react'2import { useGet } from '../../hooks/useGet';3import { getProjectStatus } from '../../selectors/get/getProjectStatus';4import { ControlModal } from './ControlModal';5import { ModalInfoProject } from './ModalInfoProject';6export const ProjectScreen = ({status}) => {7 const { data:project, loading } = useGet(getProjectStatus, status);8 9 return (10 <>11 { loading && <p className="animate__animated animate__flash">Loading</p> }12 <div className="card-columns animate__animated animate__fadeIn">13 {14 project.map(item => (15 <div className="card ms-3 animate__animated animate__fadeIn" style={ { maxWidth: 540 } }>16 <div className="row no-gutters">17 <div className="col-md-4"> 18 <img 19 src={`../../../../media/proyectos/project.png`}20 alt="member" 21 className = "card-img"22 />23 </div> 24 <div className="col-md-5">25 <div className="card-body">26 <h5 className="card-title"> {item.name} </h5>27 <p className="card-text"> {item.descr} </p>28 </div> 29 </div>30 <div className="col-md-2 mb-2">31 <ModalInfoProject key={item.id}32 {...item} />33 <ControlModal /> 34 </div> 35 36 </div>37 </div>38 ))39 }40 </div>41</>42 )...
projectAdd.js
Source:projectAdd.js
...19}202122//è·å项ç®ç¶æ23export function getProjectStatus(data){24 return request({25 url:'/bizbase/api/dic/dicdata',26 method:'get',27 params:data28 29 })30}3132//ä¿åæ·»å 项ç®33export function handleSubmitForm(data){34 return request({35 url:'/bizbase/project/create',36 method:'post',37 data38 39 })40}4142//ä¿®æ¹43export function handleModify(data){44 return request({45 url:'/bizbase/project/update',46 method:'post',47 data48 49 })50}5152//å é¤53export function projectDisable(projectId,status){54 return request({55 url:'/bizbase/project/disable',56 method:'get',57 params:{58 projectId,59 status60 }61 62 63 })64}6566// //æ¿å
æ¹å¼67// export function getProjectStatus(data){68// return request({69// url:'/bizbase/api/dic/dicdata',70// method:'get',71// params:data72 73// })74// }
...
Using AI Code Generation
1const cypress = require('cypress')2cypress.run({3 reporterOptions: {4 },5}).then((results) => {6 console.log(results)7})
Using AI Code Generation
1describe("Test", () => {2 it("Test", () => {3 cy.get(".some-class").click();4 cy.getProjectStatus().then(status => {5 console.log(status);6 });7 });8});9Cypress.Commands.add("getProjectStatus", () => {10 return new Promise(resolve => {11 const projectStatus = "some status";12 resolve(projectStatus);13 });14});
Using AI Code Generation
1describe('Cypress', function() {2 it('should be able to access the project status', function() {3 cy.log(cy.getProjectStatus())4 })5})6Cypress.Commands.add('getProjectStatus', () => {7 return Cypress.env('CYPRESS_PROJECT_STATUS')8})9{10 "env": {11 }12}13describe('Cypress', function() {14 it('should be able to access the project status', function() {15 cy.log(Cypress.env('CYPRESS_PROJECT_STATUS'))16 })17})18{19 "env": {20 }21}22describe('Cypress', function() {23 it('should be able to access the project status', function() {24 cy.log(Cypress.env('CYPRESS_PROJECT_STATUS'))25 })26})27{28 "env": {29 }30}31describe('Cypress', function() {32 it('should be able to access the project status', function() {33 cy.log(Cypress.env('CYPRESS_PROJECT_STATUS'))34 })35})36{37 "env": {38 }39}40describe('Cypress', function() {41 it('should be able to access the project status
Using AI Code Generation
1describe('Test', () => {2 it('test', () => {3 Cypress._.getProjectStatus()4 })5})6describe('Test', () => {7 it('test', () => {8 Cypress._.getProjectRoot()9 })10})11describe('Test', () => {12 it('test', () => {13 Cypress._.getProjectType()14 })15})16describe('Test', () => {17 it('test', () => {18 Cypress._.getSpecUrl()19 })20})21describe('Test', () => {22 it('test', () => {23 Cypress._.getTestConfig()24 })25})26describe('Test', () => {27 it('test', () => {28 Cypress._.getTestDisplayError()29 })30})31describe('
Using AI Code Generation
1const cypress = require('cypress');2const fs = require('fs');3(async () => {4 const results = await cypress.run({5 config: {6 },7 });8 const status = await cypress.getProjectStatus('your project id');9 fs.writeFileSync('status.json', JSON.stringify(status));10 console.log('results', results);11 console.log('status', status);12})();13{14 {15 "commit": {16 "author": {17 },18 "committer": {19 },20 },
Cypress is a renowned Javascript-based open-source, easy-to-use end-to-end testing framework primarily used for testing web applications. Cypress is a relatively new player in the automation testing space and has been gaining much traction lately, as evidenced by the number of Forks (2.7K) and Stars (42.1K) for the project. LambdaTest’s Cypress Tutorial covers step-by-step guides that will help you learn from the basics till you run automation tests on LambdaTest.
You can elevate your expertise with end-to-end testing using the Cypress automation framework and stay one step ahead in your career by earning a Cypress certification. Check out our Cypress 101 Certification.
Watch this 3 hours of complete tutorial to learn the basics of Cypress and various Cypress commands with the Cypress testing at LambdaTest.
Get 100 minutes of automation test minutes FREE!!