Commit e8de7921 authored by Cai Wei's avatar Cai Wei

feat(*): 删除无用测试脚本

parent 2c5d7b6a
Pipeline #1310 failed
stages:
- install
- lint
- test
- build
- deploy
- test
- report
# 全局变量
variables:
CYPRESS_baseUrl: "http://localhost:3000"
NODE_VERSION: "16-alpine"
CYPRESS_IMAGE: "cypress/browsers:node16.14.2-slim-chrome103-ff102"
# 1. 安装依赖
install:
stage: install
image: node:14
# 1. 构建阶段
build:
stage: build
image: node:$NODE_VERSION
script:
- npm install
- npm ci --cache .npm --prefer-offline
- npm run build
artifacts:
paths:
- node_modules/ # 保持依赖缓存供后续阶段使用
expire_in: 1h
# 2. 代码检查
lint:
stage: lint
image: node:14
dependencies:
- install
script:
- npm run lint
- dist/
- node_modules/
expire_in: 1 hour
cache:
paths:
- .npm/
- node_modules/
only:
- web
- projects
- merge_requests
# 3. 单元测试
unit_test:
# 2. 基础测试
test:basic:
stage: test
image: node:14
image: $CYPRESS_IMAGE
dependencies:
- install
- build
script:
- npm run test:unit
- npm run preview -- --host 0.0.0.0 --port 3000 &
- npx wait-on http://localhost:3000
- npx cypress run --browser chrome --spec "cypress/e2e/spec.cy.js" --reporter mochawesome --reporter-options "reportDir=cypress/reports/basic,overwrite=false,html=true,json=true"
artifacts:
when: always
reports:
junit: junit.xml
paths:
- cypress/reports/
- cypress/videos/
- cypress/screenshots/
expire_in: 1 week
only:
- web
- projects
- merge_requests
# 4. Cypress E2E 测试
e2e_test:
stage: test
image: cypress/included:12.7.0 # 内置 Cypress + 浏览器环境
# 3. 生成测试报告
report:
stage: report
image: node:$NODE_VERSION
dependencies:
- install
- test:basic
script:
- npm run build # 先打包前端
- npx http-server ./dist -p 8080 & # 启动测试服务
- npx wait-on http://localhost:8080
- npx cypress run --browser chrome
- npm install -g mochawesome-merge mochawesome-report-generator
- mkdir -p public/reports
- mochawesome-merge "cypress/reports/**/*.json" > public/reports/merged-report.json
- marge public/reports/merged-report.json --reportDir public/reports --inline --reportTitle "DC-TOM Cypress 测试报告"
- cp -r cypress/videos public/ || echo "无视频文件"
- cp -r cypress/screenshots public/ || echo "无截图文件"
artifacts:
when: always
paths:
- cypress/videos
- cypress/screenshots
- public/
expire_in: 1 week
only:
- web
- projects
- merge_requests
# 5. 构建产物
build:
stage: build
image: node:14
# GitLab Pages 部署
pages:
stage: report
dependencies:
- install
- report
script:
- npm run build
- echo "部署测试报告到 GitLab Pages"
artifacts:
paths:
- dist/
# 6. 部署
deploy:
stage: deploy
image: alpine:latest
dependencies:
- build
script:
- echo "Deploying..."
# 这里替换成实际的部署命令
- public/
only:
- projects
/// <reference types="cypress" />
// Welcome to Cypress!
//
// This spec file contains a variety of sample tests
// for a todo list app that are designed to demonstrate
// the power of writing tests in Cypress.
//
// To learn more about how Cypress works and
// what makes it such an awesome testing tool,
// please read our getting started guide:
// https://on.cypress.io/introduction-to-cypress
describe('example to-do app', () => {
beforeEach(() => {
// Cypress starts out with a blank slate for each test
// so we must tell it to visit our website with the `cy.visit()` command.
// Since we want to visit the same URL at the start of all our tests,
// we include it in our beforeEach function so that it runs before each test
cy.visit('https://example.cypress.io/todo')
})
it('displays two todo items by default', () => {
// We use the `cy.get()` command to get all elements that match the selector.
// Then, we use `should` to assert that there are two matched items,
// which are the two default items.
cy.get('.todo-list li').should('have.length', 2)
// We can go even further and check that the default todos each contain
// the correct text. We use the `first` and `last` functions
// to get just the first and last matched elements individually,
// and then perform an assertion with `should`.
cy.get('.todo-list li').first().should('have.text', 'Pay electric bill')
cy.get('.todo-list li').last().should('have.text', 'Walk the dog')
})
it('can add new todo items', () => {
// We'll store our item text in a variable so we can reuse it
const newItem = 'Feed the cat'
// Let's get the input element and use the `type` command to
// input our new list item. After typing the content of our item,
// we need to type the enter key as well in order to submit the input.
// This input has a data-test attribute so we'll use that to select the
// element in accordance with best practices:
// https://on.cypress.io/selecting-elements
cy.get('[data-test=new-todo]').type(`${newItem}{enter}`)
// Now that we've typed our new item, let's check that it actually was added to the list.
// Since it's the newest item, it should exist as the last element in the list.
// In addition, with the two default items, we should have a total of 3 elements in the list.
// Since assertions yield the element that was asserted on,
// we can chain both of these assertions together into a single statement.
cy.get('.todo-list li')
.should('have.length', 3)
.last()
.should('have.text', newItem)
})
it('can check off an item as completed', () => {
// In addition to using the `get` command to get an element by selector,
// we can also use the `contains` command to get an element by its contents.
// However, this will yield the <label>, which is lowest-level element that contains the text.
// In order to check the item, we'll find the <input> element for this <label>
// by traversing up the dom to the parent element. From there, we can `find`
// the child checkbox <input> element and use the `check` command to check it.
cy.contains('Pay electric bill')
.parent()
.find('input[type=checkbox]')
.check()
// Now that we've checked the button, we can go ahead and make sure
// that the list element is now marked as completed.
// Again we'll use `contains` to find the <label> element and then use the `parents` command
// to traverse multiple levels up the dom until we find the corresponding <li> element.
// Once we get that element, we can assert that it has the completed class.
cy.contains('Pay electric bill')
.parents('li')
.should('have.class', 'completed')
})
context('with a checked task', () => {
beforeEach(() => {
// We'll take the command we used above to check off an element
// Since we want to perform multiple tests that start with checking
// one element, we put it in the beforeEach hook
// so that it runs at the start of every test.
cy.contains('Pay electric bill')
.parent()
.find('input[type=checkbox]')
.check()
})
it('can filter for uncompleted tasks', () => {
// We'll click on the "active" button in order to
// display only incomplete items
cy.contains('Active').click()
// After filtering, we can assert that there is only the one
// incomplete item in the list.
cy.get('.todo-list li')
.should('have.length', 1)
.first()
.should('have.text', 'Walk the dog')
// For good measure, let's also assert that the task we checked off
// does not exist on the page.
cy.contains('Pay electric bill').should('not.exist')
})
it('can filter for completed tasks', () => {
// We can perform similar steps as the test above to ensure
// that only completed tasks are shown
cy.contains('Completed').click()
cy.get('.todo-list li')
.should('have.length', 1)
.first()
.should('have.text', 'Pay electric bill')
cy.contains('Walk the dog').should('not.exist')
})
it('can delete all completed tasks', () => {
// First, let's click the "Clear completed" button
// `contains` is actually serving two purposes here.
// First, it's ensuring that the button exists within the dom.
// This button only appears when at least one task is checked
// so this command is implicitly verifying that it does exist.
// Second, it selects the button so we can click it.
cy.contains('Clear completed').click()
// Then we can make sure that there is only one element
// in the list and our element does not exist
cy.get('.todo-list li')
.should('have.length', 1)
.should('not.have.text', 'Pay electric bill')
// Finally, make sure that the clear button no longer exists.
cy.contains('Clear completed').should('not.exist')
})
})
})
This diff is collapsed.
/// <reference types="cypress" />
context('Aliasing', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/aliasing')
})
it('.as() - alias a DOM element for later use', () => {
// https://on.cypress.io/as
// Alias a DOM element for use later
// We don't have to traverse to the element
// later in our code, we reference it with @
cy.get('.as-table').find('tbody>tr')
.first().find('td').first()
.find('button').as('firstBtn')
// when we reference the alias, we place an
// @ in front of its name
cy.get('@firstBtn').click()
cy.get('@firstBtn')
.should('have.class', 'btn-success')
.and('contain', 'Changed')
})
it('.as() - alias a route for later use', () => {
// Alias the route to wait for its response
cy.intercept('GET', '**/comments/*').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.network-btn').click()
// https://on.cypress.io/wait
cy.wait('@getComment').its('response.statusCode').should('eq', 200)
})
})
/// <reference types="cypress" />
context('Assertions', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/assertions')
})
describe('Implicit Assertions', () => {
it('.should() - make an assertion about the current subject', () => {
// https://on.cypress.io/should
cy.get('.assertion-table')
.find('tbody tr:last')
.should('have.class', 'success')
.find('td')
.first()
// checking the text of the <td> element in various ways
.should('have.text', 'Column content')
.should('contain', 'Column content')
.should('have.html', 'Column content')
// chai-jquery uses "is()" to check if element matches selector
.should('match', 'td')
// to match text content against a regular expression
// first need to invoke jQuery method text()
// and then match using regular expression
.invoke('text')
.should('match', /column content/i)
// a better way to check element's text content against a regular expression
// is to use "cy.contains"
// https://on.cypress.io/contains
cy.get('.assertion-table')
.find('tbody tr:last')
// finds first <td> element with text content matching regular expression
.contains('td', /column content/i)
.should('be.visible')
// for more information about asserting element's text
// see https://on.cypress.io/using-cypress-faq#How-do-I-get-an-element’s-text-contents
})
it('.and() - chain multiple assertions together', () => {
// https://on.cypress.io/and
cy.get('.assertions-link')
.should('have.class', 'active')
.and('have.attr', 'href')
.and('include', 'cypress.io')
})
})
describe('Explicit Assertions', () => {
// https://on.cypress.io/assertions
it('expect - make an assertion about a specified subject', () => {
// We can use Chai's BDD style assertions
expect(true).to.be.true
const o = { foo: 'bar' }
expect(o).to.equal(o)
expect(o).to.deep.equal({ foo: 'bar' })
// matching text using regular expression
expect('FooBar').to.match(/bar$/i)
})
it('pass your own callback function to should()', () => {
// Pass a function to should that can have any number
// of explicit assertions within it.
// The ".should(cb)" function will be retried
// automatically until it passes all your explicit assertions or times out.
cy.get('.assertions-p')
.find('p')
.should(($p) => {
// https://on.cypress.io/$
// return an array of texts from all of the p's
const texts = $p.map((i, el) => Cypress.$(el).text())
// jquery map returns jquery object
// and .get() convert this to simple array
const paragraphs = texts.get()
// array should have length of 3
expect(paragraphs, 'has 3 paragraphs').to.have.length(3)
// use second argument to expect(...) to provide clear
// message with each assertion
expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([
'Some text from first p',
'More text from second p',
'And even more text from third p',
])
})
})
it('finds element by class name regex', () => {
cy.get('.docs-header')
.find('div')
// .should(cb) callback function will be retried
.should(($div) => {
expect($div).to.have.length(1)
const className = $div[0].className
expect(className).to.match(/heading-/)
})
// .then(cb) callback is not retried,
// it either passes or fails
.then(($div) => {
expect($div, 'text content').to.have.text('Introduction')
})
})
it('can throw any error', () => {
cy.get('.docs-header')
.find('div')
.should(($div) => {
if ($div.length !== 1) {
// you can throw your own errors
throw new Error('Did not find 1 element')
}
const className = $div[0].className
if (!className.match(/heading-/)) {
throw new Error(`Could not find class "heading-" in ${className}`)
}
})
})
it('matches unknown text between two elements', () => {
/**
* Text from the first element.
* @type {string}
*/
let text
/**
* Normalizes passed text,
* useful before comparing text with spaces and different capitalization.
* @param {string} s Text to normalize
*/
const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase()
cy.get('.two-elements')
.find('.first')
.then(($first) => {
// save text from the first element
text = normalizeText($first.text())
})
cy.get('.two-elements')
.find('.second')
.should(($div) => {
// we can massage text before comparing
const secondText = normalizeText($div.text())
expect(secondText, 'second text').to.equal(text)
})
})
it('assert - assert shape of an object', () => {
const person = {
name: 'Joe',
age: 20,
}
assert.isObject(person, 'value is object')
})
it('retries the should callback until assertions pass', () => {
cy.get('#random-number')
.should(($div) => {
const n = parseFloat($div.text())
expect(n).to.be.gte(1).and.be.lte(10)
})
})
})
})
/// <reference types="cypress" />
context('Connectors', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/connectors')
})
it('.each() - iterate over an array of elements', () => {
// https://on.cypress.io/each
cy.get('.connectors-each-ul>li')
.each(($el, index, $list) => {
console.log($el, index, $list)
})
})
it('.its() - get properties on the current subject', () => {
// https://on.cypress.io/its
cy.get('.connectors-its-ul>li')
// calls the 'length' property yielding that value
.its('length')
.should('be.gt', 2)
})
it('.invoke() - invoke a function on the current subject', () => {
// our div is hidden in our script.js
// $('.connectors-div').hide()
cy.get('.connectors-div').should('be.hidden')
// https://on.cypress.io/invoke
// call the jquery method 'show' on the 'div.container'
cy.get('.connectors-div').invoke('show')
cy.get('.connectors-div').should('be.visible')
})
it('.spread() - spread an array as individual args to callback function', () => {
// https://on.cypress.io/spread
const arr = ['foo', 'bar', 'baz']
cy.wrap(arr).spread((foo, bar, baz) => {
expect(foo).to.eq('foo')
expect(bar).to.eq('bar')
expect(baz).to.eq('baz')
})
})
describe('.then()', () => {
it('invokes a callback function with the current subject', () => {
// https://on.cypress.io/then
cy.get('.connectors-list > li')
.then(($lis) => {
expect($lis, '3 items').to.have.length(3)
expect($lis.eq(0), 'first item').to.contain('Walk the dog')
expect($lis.eq(1), 'second item').to.contain('Feed the cat')
expect($lis.eq(2), 'third item').to.contain('Write JavaScript')
})
})
it('yields the returned value to the next command', () => {
cy.wrap(1)
.then((num) => {
expect(num).to.equal(1)
return 2
})
.then((num) => {
expect(num).to.equal(2)
})
})
it('yields the original subject without return', () => {
cy.wrap(1)
.then((num) => {
expect(num).to.equal(1)
// note that nothing is returned from this callback
})
.then((num) => {
// this callback receives the original unchanged value 1
expect(num).to.equal(1)
})
})
it('yields the value yielded by the last Cypress command inside', () => {
cy.wrap(1)
.then((num) => {
expect(num).to.equal(1)
// note how we run a Cypress command
// the result yielded by this Cypress command
// will be passed to the second ".then"
cy.wrap(2)
})
.then((num) => {
// this callback receives the value yielded by "cy.wrap(2)"
expect(num).to.equal(2)
})
})
})
})
/// <reference types="cypress" />
context('Cookies', () => {
beforeEach(() => {
Cypress.Cookies.debug(true)
cy.visit('https://example.cypress.io/commands/cookies')
// clear cookies again after visiting to remove
// any 3rd party cookies picked up such as cloudflare
cy.clearCookies()
})
it('cy.getCookie() - get a browser cookie', () => {
// https://on.cypress.io/getcookie
cy.get('#getCookie .set-a-cookie').click()
// cy.getCookie() yields a cookie object
cy.getCookie('token').should('have.property', 'value', '123ABC')
})
it('cy.getCookies() - get browser cookies for the current domain', () => {
// https://on.cypress.io/getcookies
cy.getCookies().should('be.empty')
cy.get('#getCookies .set-a-cookie').click()
// cy.getCookies() yields an array of cookies
cy.getCookies().should('have.length', 1).should((cookies) => {
// each cookie has these properties
expect(cookies[0]).to.have.property('name', 'token')
expect(cookies[0]).to.have.property('value', '123ABC')
expect(cookies[0]).to.have.property('httpOnly', false)
expect(cookies[0]).to.have.property('secure', false)
expect(cookies[0]).to.have.property('domain')
expect(cookies[0]).to.have.property('path')
})
})
it('cy.getAllCookies() - get all browser cookies', () => {
// https://on.cypress.io/getallcookies
cy.getAllCookies().should('be.empty')
cy.setCookie('key', 'value')
cy.setCookie('key', 'value', { domain: '.example.com' })
// cy.getAllCookies() yields an array of cookies
cy.getAllCookies().should('have.length', 2).should((cookies) => {
// each cookie has these properties
expect(cookies[0]).to.have.property('name', 'key')
expect(cookies[0]).to.have.property('value', 'value')
expect(cookies[0]).to.have.property('httpOnly', false)
expect(cookies[0]).to.have.property('secure', false)
expect(cookies[0]).to.have.property('domain')
expect(cookies[0]).to.have.property('path')
expect(cookies[1]).to.have.property('name', 'key')
expect(cookies[1]).to.have.property('value', 'value')
expect(cookies[1]).to.have.property('httpOnly', false)
expect(cookies[1]).to.have.property('secure', false)
expect(cookies[1]).to.have.property('domain', '.example.com')
expect(cookies[1]).to.have.property('path')
})
})
it('cy.setCookie() - set a browser cookie', () => {
// https://on.cypress.io/setcookie
cy.getCookies().should('be.empty')
cy.setCookie('foo', 'bar')
// cy.getCookie() yields a cookie object
cy.getCookie('foo').should('have.property', 'value', 'bar')
})
it('cy.clearCookie() - clear a browser cookie', () => {
// https://on.cypress.io/clearcookie
cy.getCookie('token').should('be.null')
cy.get('#clearCookie .set-a-cookie').click()
cy.getCookie('token').should('have.property', 'value', '123ABC')
// cy.clearCookies() yields null
cy.clearCookie('token')
cy.getCookie('token').should('be.null')
})
it('cy.clearCookies() - clear browser cookies for the current domain', () => {
// https://on.cypress.io/clearcookies
cy.getCookies().should('be.empty')
cy.get('#clearCookies .set-a-cookie').click()
cy.getCookies().should('have.length', 1)
// cy.clearCookies() yields null
cy.clearCookies()
cy.getCookies().should('be.empty')
})
it('cy.clearAllCookies() - clear all browser cookies', () => {
// https://on.cypress.io/clearallcookies
cy.getAllCookies().should('be.empty')
cy.setCookie('key', 'value')
cy.setCookie('key', 'value', { domain: '.example.com' })
cy.getAllCookies().should('have.length', 2)
// cy.clearAllCookies() yields null
cy.clearAllCookies()
cy.getAllCookies().should('be.empty')
})
})
/// <reference types="cypress" />
context('Cypress APIs', () => {
context('Cypress.Commands', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// https://on.cypress.io/custom-commands
it('.add() - create a custom command', () => {
Cypress.Commands.add('console', {
prevSubject: true,
}, (subject, method) => {
// the previous subject is automatically received
// and the commands arguments are shifted
// allow us to change the console method used
method = method || 'log'
// log the subject to the console
console[method]('The subject is', subject)
// whatever we return becomes the new subject
// we don't want to change the subject so
// we return whatever was passed in
return subject
})
cy.get('button').console('info').then(($button) => {
// subject is still $button
})
})
})
context('Cypress.Cookies', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// https://on.cypress.io/cookies
it('.debug() - enable or disable debugging', () => {
Cypress.Cookies.debug(true)
// Cypress will now log in the console when
// cookies are set or cleared
cy.setCookie('fakeCookie', '123ABC')
cy.clearCookie('fakeCookie')
cy.setCookie('fakeCookie', '123ABC')
cy.clearCookie('fakeCookie')
cy.setCookie('fakeCookie', '123ABC')
})
})
context('Cypress.arch', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get CPU architecture name of underlying OS', () => {
// https://on.cypress.io/arch
expect(Cypress.arch).to.exist
})
})
context('Cypress.config()', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get and set configuration options', () => {
// https://on.cypress.io/config
let myConfig = Cypress.config()
expect(myConfig).to.have.property('animationDistanceThreshold', 5)
expect(myConfig).to.have.property('baseUrl', null)
expect(myConfig).to.have.property('defaultCommandTimeout', 4000)
expect(myConfig).to.have.property('requestTimeout', 5000)
expect(myConfig).to.have.property('responseTimeout', 30000)
expect(myConfig).to.have.property('viewportHeight', 660)
expect(myConfig).to.have.property('viewportWidth', 1000)
expect(myConfig).to.have.property('pageLoadTimeout', 60000)
expect(myConfig).to.have.property('waitForAnimations', true)
expect(Cypress.config('pageLoadTimeout')).to.eq(60000)
// this will change the config for the rest of your tests!
Cypress.config('pageLoadTimeout', 20000)
expect(Cypress.config('pageLoadTimeout')).to.eq(20000)
Cypress.config('pageLoadTimeout', 60000)
})
})
context('Cypress.dom', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// https://on.cypress.io/dom
it('.isHidden() - determine if a DOM element is hidden', () => {
let hiddenP = Cypress.$('.dom-p p.hidden').get(0)
let visibleP = Cypress.$('.dom-p p.visible').get(0)
// our first paragraph has css class 'hidden'
expect(Cypress.dom.isHidden(hiddenP)).to.be.true
expect(Cypress.dom.isHidden(visibleP)).to.be.false
})
})
context('Cypress.env()', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// We can set environment variables for highly dynamic values
// https://on.cypress.io/environment-variables
it('Get environment variables', () => {
// https://on.cypress.io/env
// set multiple environment variables
Cypress.env({
host: 'veronica.dev.local',
api_server: 'http://localhost:8888/v1/',
})
// get environment variable
expect(Cypress.env('host')).to.eq('veronica.dev.local')
// set environment variable
Cypress.env('api_server', 'http://localhost:8888/v2/')
expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/')
// get all environment variable
expect(Cypress.env()).to.have.property('host', 'veronica.dev.local')
expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/')
})
})
context('Cypress.log', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Control what is printed to the Command Log', () => {
// https://on.cypress.io/cypress-log
})
})
context('Cypress.platform', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get underlying OS name', () => {
// https://on.cypress.io/platform
expect(Cypress.platform).to.be.exist
})
})
context('Cypress.version', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get current version of Cypress being run', () => {
// https://on.cypress.io/version
expect(Cypress.version).to.be.exist
})
})
context('Cypress.spec', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get current spec information', () => {
// https://on.cypress.io/spec
// wrap the object so we can inspect it easily by clicking in the command log
cy.wrap(Cypress.spec).should('include.keys', ['name', 'relative', 'absolute'])
})
})
})
/// <reference types="cypress" />
/// JSON fixture file can be loaded directly using
// the built-in JavaScript bundler
const requiredExample = require('../../fixtures/example')
context('Files', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/files')
// load example.json fixture file and store
// in the test context object
cy.fixture('example.json').as('example')
})
it('cy.fixture() - load a fixture', () => {
// https://on.cypress.io/fixture
// Instead of writing a response inline you can
// use a fixture file's content.
// when application makes an Ajax request matching "GET **/comments/*"
// Cypress will intercept it and reply with the object in `example.json` fixture
cy.intercept('GET', '**/comments/*', { fixture: 'example.json' }).as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.fixture-btn').click()
cy.wait('@getComment').its('response.body')
.should('have.property', 'name')
.and('include', 'Using fixtures to represent data')
})
it('cy.fixture() or require - load a fixture', function () {
// we are inside the "function () { ... }"
// callback and can use test context object "this"
// "this.example" was loaded in "beforeEach" function callback
expect(this.example, 'fixture in the test context')
.to.deep.equal(requiredExample)
// or use "cy.wrap" and "should('deep.equal', ...)" assertion
cy.wrap(this.example)
.should('deep.equal', requiredExample)
})
it('cy.readFile() - read file contents', () => {
// https://on.cypress.io/readfile
// You can read a file and yield its contents
// The filePath is relative to your project's root.
cy.readFile(Cypress.config('configFile')).then((config) => {
expect(config).to.be.an('string')
})
})
it('cy.writeFile() - write to a file', () => {
// https://on.cypress.io/writefile
// You can write to a file
// Use a response from a request to automatically
// generate a fixture file for use later
cy.request('https://jsonplaceholder.cypress.io/users')
.then((response) => {
cy.writeFile('cypress/fixtures/users.json', response.body)
})
cy.fixture('users').should((users) => {
expect(users[0].name).to.exist
})
// JavaScript arrays and objects are stringified
// and formatted into text.
cy.writeFile('cypress/fixtures/profile.json', {
id: 8739,
name: 'Jane',
email: 'jane@example.com',
})
cy.fixture('profile').should((profile) => {
expect(profile.name).to.eq('Jane')
})
})
})
/// <reference types="cypress" />
context('Location', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/location')
})
it('cy.hash() - get the current URL hash', () => {
// https://on.cypress.io/hash
cy.hash().should('be.empty')
})
it('cy.location() - get window.location', () => {
// https://on.cypress.io/location
cy.location().should((location) => {
expect(location.hash).to.be.empty
expect(location.href).to.eq('https://example.cypress.io/commands/location')
expect(location.host).to.eq('example.cypress.io')
expect(location.hostname).to.eq('example.cypress.io')
expect(location.origin).to.eq('https://example.cypress.io')
expect(location.pathname).to.eq('/commands/location')
expect(location.port).to.eq('')
expect(location.protocol).to.eq('https:')
expect(location.search).to.be.empty
})
})
it('cy.url() - get the current URL', () => {
// https://on.cypress.io/url
cy.url().should('eq', 'https://example.cypress.io/commands/location')
})
})
/// <reference types="cypress" />
context('Misc', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/misc')
})
it('cy.exec() - execute a system command', () => {
// execute a system command.
// so you can take actions necessary for
// your test outside the scope of Cypress.
// https://on.cypress.io/exec
// we can use Cypress.platform string to
// select appropriate command
// https://on.cypress/io/platform
cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`)
// on CircleCI Windows build machines we have a failure to run bash shell
// https://github.com/cypress-io/cypress/issues/5169
// so skip some of the tests by passing flag "--env circle=true"
const isCircleOnWindows = Cypress.platform === 'win32' && Cypress.env('circle')
if (isCircleOnWindows) {
cy.log('Skipping test on CircleCI')
return
}
// cy.exec problem on Shippable CI
// https://github.com/cypress-io/cypress/issues/6718
const isShippable = Cypress.platform === 'linux' && Cypress.env('shippable')
if (isShippable) {
cy.log('Skipping test on ShippableCI')
return
}
cy.exec('echo Jane Lane')
.its('stdout').should('contain', 'Jane Lane')
if (Cypress.platform === 'win32') {
cy.exec(`print ${Cypress.config('configFile')}`)
.its('stderr').should('be.empty')
} else {
cy.exec(`cat ${Cypress.config('configFile')}`)
.its('stderr').should('be.empty')
cy.exec('pwd')
.its('code').should('eq', 0)
}
})
it('cy.focused() - get the DOM element that has focus', () => {
// https://on.cypress.io/focused
cy.get('.misc-form').find('#name').click()
cy.focused().should('have.id', 'name')
cy.get('.misc-form').find('#description').click()
cy.focused().should('have.id', 'description')
})
context('Cypress.Screenshot', function () {
it('cy.screenshot() - take a screenshot', () => {
// https://on.cypress.io/screenshot
cy.screenshot('my-image')
})
it('Cypress.Screenshot.defaults() - change default config of screenshots', function () {
Cypress.Screenshot.defaults({
blackout: ['.foo'],
capture: 'viewport',
clip: { x: 0, y: 0, width: 200, height: 200 },
scale: false,
disableTimersAndAnimations: true,
screenshotOnRunFailure: true,
onBeforeScreenshot () { },
onAfterScreenshot () { },
})
})
})
it('cy.wrap() - wrap an object', () => {
// https://on.cypress.io/wrap
cy.wrap({ foo: 'bar' })
.should('have.property', 'foo')
.and('include', 'bar')
})
})
/// <reference types="cypress" />
context('Navigation', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io')
cy.get('.navbar-nav').contains('Commands').click()
cy.get('.dropdown-menu').contains('Navigation').click()
})
it('cy.go() - go back or forward in the browser\'s history', () => {
// https://on.cypress.io/go
cy.location('pathname').should('include', 'navigation')
cy.go('back')
cy.location('pathname').should('not.include', 'navigation')
cy.go('forward')
cy.location('pathname').should('include', 'navigation')
// clicking back
cy.go(-1)
cy.location('pathname').should('not.include', 'navigation')
// clicking forward
cy.go(1)
cy.location('pathname').should('include', 'navigation')
})
it('cy.reload() - reload the page', () => {
// https://on.cypress.io/reload
cy.reload()
// reload the page without using the cache
cy.reload(true)
})
it('cy.visit() - visit a remote url', () => {
// https://on.cypress.io/visit
// Visit any sub-domain of your current domain
// Pass options to the visit
cy.visit('https://example.cypress.io/commands/navigation', {
timeout: 50000, // increase total time for the visit to resolve
onBeforeLoad (contentWindow) {
// contentWindow is the remote page's window object
expect(typeof contentWindow === 'object').to.be.true
},
onLoad (contentWindow) {
// contentWindow is the remote page's window object
expect(typeof contentWindow === 'object').to.be.true
},
})
})
})
/// <reference types="cypress" />
context('Network Requests', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/network-requests')
})
// Manage HTTP requests in your app
it('cy.request() - make an XHR request', () => {
// https://on.cypress.io/request
cy.request('https://jsonplaceholder.cypress.io/comments')
.should((response) => {
expect(response.status).to.eq(200)
// the server sometimes gets an extra comment posted from another machine
// which gets returned as 1 extra object
expect(response.body).to.have.property('length').and.be.oneOf([500, 501])
expect(response).to.have.property('headers')
expect(response).to.have.property('duration')
})
})
it('cy.request() - verify response using BDD syntax', () => {
cy.request('https://jsonplaceholder.cypress.io/comments')
.then((response) => {
// https://on.cypress.io/assertions
expect(response).property('status').to.equal(200)
expect(response).property('body').to.have.property('length').and.be.oneOf([500, 501])
expect(response).to.include.keys('headers', 'duration')
})
})
it('cy.request() with query parameters', () => {
// will execute request
// https://jsonplaceholder.cypress.io/comments?postId=1&id=3
cy.request({
url: 'https://jsonplaceholder.cypress.io/comments',
qs: {
postId: 1,
id: 3,
},
})
.its('body')
.should('be.an', 'array')
.and('have.length', 1)
.its('0') // yields first element of the array
.should('contain', {
postId: 1,
id: 3,
})
})
it('cy.request() - pass result to the second request', () => {
// first, let's find out the userId of the first user we have
cy.request('https://jsonplaceholder.cypress.io/users?_limit=1')
.its('body') // yields the response object
.its('0') // yields the first element of the returned list
// the above two commands its('body').its('0')
// can be written as its('body.0')
// if you do not care about TypeScript checks
.then((user) => {
expect(user).property('id').to.be.a('number')
// make a new post on behalf of the user
cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', {
userId: user.id,
title: 'Cypress Test Runner',
body: 'Fast, easy and reliable testing for anything that runs in a browser.',
})
})
// note that the value here is the returned value of the 2nd request
// which is the new post object
.then((response) => {
expect(response).property('status').to.equal(201) // new entity created
expect(response).property('body').to.contain({
title: 'Cypress Test Runner',
})
// we don't know the exact post id - only that it will be > 100
// since JSONPlaceholder has built-in 100 posts
expect(response.body).property('id').to.be.a('number')
.and.to.be.gt(100)
// we don't know the user id here - since it was in above closure
// so in this test just confirm that the property is there
expect(response.body).property('userId').to.be.a('number')
})
})
it('cy.request() - save response in the shared test context', () => {
// https://on.cypress.io/variables-and-aliases
cy.request('https://jsonplaceholder.cypress.io/users?_limit=1')
.its('body').its('0') // yields the first element of the returned list
.as('user') // saves the object in the test context
.then(function () {
// NOTE 👀
// By the time this callback runs the "as('user')" command
// has saved the user object in the test context.
// To access the test context we need to use
// the "function () { ... }" callback form,
// otherwise "this" points at a wrong or undefined object!
cy.request('POST', 'https://jsonplaceholder.cypress.io/posts', {
userId: this.user.id,
title: 'Cypress Test Runner',
body: 'Fast, easy and reliable testing for anything that runs in a browser.',
})
.its('body').as('post') // save the new post from the response
})
.then(function () {
// When this callback runs, both "cy.request" API commands have finished
// and the test context has "user" and "post" objects set.
// Let's verify them.
expect(this.post, 'post has the right user id').property('userId').to.equal(this.user.id)
})
})
it('cy.intercept() - route responses to matching requests', () => {
// https://on.cypress.io/intercept
let message = 'whoa, this comment does not exist'
// Listen to GET to comments/1
cy.intercept('GET', '**/comments/*').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.network-btn').click()
// https://on.cypress.io/wait
cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304])
// Listen to POST to comments
cy.intercept('POST', '**/comments').as('postComment')
// we have code that posts a comment when
// the button is clicked in scripts.js
cy.get('.network-post').click()
cy.wait('@postComment').should(({ request, response }) => {
expect(request.body).to.include('email')
expect(request.headers).to.have.property('content-type')
expect(response && response.body).to.have.property('name', 'Using POST in cy.intercept()')
})
// Stub a response to PUT comments/ ****
cy.intercept({
method: 'PUT',
url: '**/comments/*',
}, {
statusCode: 404,
body: { error: message },
headers: { 'access-control-allow-origin': '*' },
delayMs: 500,
}).as('putComment')
// we have code that puts a comment when
// the button is clicked in scripts.js
cy.get('.network-put').click()
cy.wait('@putComment')
// our 404 statusCode logic in scripts.js executed
cy.get('.network-put-comment').should('contain', message)
})
})
/// <reference types="cypress" />
context('Querying', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/querying')
})
// The most commonly used query is 'cy.get()', you can
// think of this like the '$' in jQuery
it('cy.get() - query DOM elements', () => {
// https://on.cypress.io/get
cy.get('#query-btn').should('contain', 'Button')
cy.get('.query-btn').should('contain', 'Button')
cy.get('#querying .well>button:first').should('contain', 'Button')
// ↲
// Use CSS selectors just like jQuery
cy.get('[data-test-id="test-example"]').should('have.class', 'example')
// 'cy.get()' yields jQuery object, you can get its attribute
// by invoking `.attr()` method
cy.get('[data-test-id="test-example"]')
.invoke('attr', 'data-test-id')
.should('equal', 'test-example')
// or you can get element's CSS property
cy.get('[data-test-id="test-example"]')
.invoke('css', 'position')
.should('equal', 'static')
// or use assertions directly during 'cy.get()'
// https://on.cypress.io/assertions
cy.get('[data-test-id="test-example"]')
.should('have.attr', 'data-test-id', 'test-example')
.and('have.css', 'position', 'static')
})
it('cy.contains() - query DOM elements with matching content', () => {
// https://on.cypress.io/contains
cy.get('.query-list')
.contains('bananas')
.should('have.class', 'third')
// we can pass a regexp to `.contains()`
cy.get('.query-list')
.contains(/^b\w+/)
.should('have.class', 'third')
cy.get('.query-list')
.contains('apples')
.should('have.class', 'first')
// passing a selector to contains will
// yield the selector containing the text
cy.get('#querying')
.contains('ul', 'oranges')
.should('have.class', 'query-list')
cy.get('.query-button')
.contains('Save Form')
.should('have.class', 'btn')
})
it('.within() - query DOM elements within a specific element', () => {
// https://on.cypress.io/within
cy.get('.query-form').within(() => {
cy.get('input:first').should('have.attr', 'placeholder', 'Email')
cy.get('input:last').should('have.attr', 'placeholder', 'Password')
})
})
it('cy.root() - query the root DOM element', () => {
// https://on.cypress.io/root
// By default, root is the document
cy.root().should('match', 'html')
cy.get('.query-ul').within(() => {
// In this within, the root is now the ul DOM element
cy.root().should('have.class', 'query-ul')
})
})
it('best practices - selecting elements', () => {
// https://on.cypress.io/best-practices#Selecting-Elements
cy.get('[data-cy=best-practices-selecting-elements]').within(() => {
// Worst - too generic, no context
cy.get('button').click()
// Bad. Coupled to styling. Highly subject to change.
cy.get('.btn.btn-large').click()
// Average. Coupled to the `name` attribute which has HTML semantics.
cy.get('[name=submission]').click()
// Better. But still coupled to styling or JS event listeners.
cy.get('#main').click()
// Slightly better. Uses an ID but also ensures the element
// has an ARIA role attribute
cy.get('#main[role=button]').click()
// Much better. But still coupled to text content that may change.
cy.contains('Submit').click()
// Best. Insulated from all changes.
cy.get('[data-cy=submit]').click()
})
})
})
/// <reference types="cypress" />
context('Spies, Stubs, and Clock', () => {
it('cy.spy() - wrap a method in a spy', () => {
// https://on.cypress.io/spy
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
const obj = {
foo () {},
}
const spy = cy.spy(obj, 'foo').as('anyArgs')
obj.foo()
expect(spy).to.be.called
})
it('cy.spy() retries until assertions pass', () => {
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
const obj = {
/**
* Prints the argument passed
* @param x {any}
*/
foo (x) {
console.log('obj.foo called with', x)
},
}
cy.spy(obj, 'foo').as('foo')
setTimeout(() => {
obj.foo('first')
}, 500)
setTimeout(() => {
obj.foo('second')
}, 2500)
cy.get('@foo').should('have.been.calledTwice')
})
it('cy.stub() - create a stub and/or replace a function with stub', () => {
// https://on.cypress.io/stub
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
const obj = {
/**
* prints both arguments to the console
* @param a {string}
* @param b {string}
*/
foo (a, b) {
console.log('a', a, 'b', b)
},
}
const stub = cy.stub(obj, 'foo').as('foo')
obj.foo('foo', 'bar')
expect(stub).to.be.called
})
it('cy.clock() - control time in the browser', () => {
// https://on.cypress.io/clock
// create the date in UTC so it's always the same
// no matter what local timezone the browser is running in
const now = new Date(Date.UTC(2017, 2, 14)).getTime()
cy.clock(now)
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
cy.get('#clock-div').click()
cy.get('#clock-div')
.should('have.text', '1489449600')
})
it('cy.tick() - move time in the browser', () => {
// https://on.cypress.io/tick
// create the date in UTC so it's always the same
// no matter what local timezone the browser is running in
const now = new Date(Date.UTC(2017, 2, 14)).getTime()
cy.clock(now)
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
cy.get('#tick-div').click()
cy.get('#tick-div')
.should('have.text', '1489449600')
cy.tick(10000) // 10 seconds passed
cy.get('#tick-div').click()
cy.get('#tick-div')
.should('have.text', '1489449610')
})
it('cy.stub() matches depending on arguments', () => {
// see all possible matchers at
// https://sinonjs.org/releases/latest/matchers/
const greeter = {
/**
* Greets a person
* @param {string} name
*/
greet (name) {
return `Hello, ${name}!`
},
}
cy.stub(greeter, 'greet')
.callThrough() // if you want non-matched calls to call the real method
.withArgs(Cypress.sinon.match.string).returns('Hi')
.withArgs(Cypress.sinon.match.number).throws(new Error('Invalid name'))
expect(greeter.greet('World')).to.equal('Hi')
expect(() => greeter.greet(42)).to.throw('Invalid name')
expect(greeter.greet).to.have.been.calledTwice
// non-matched calls goes the actual method
expect(greeter.greet()).to.equal('Hello, undefined!')
})
it('matches call arguments using Sinon matchers', () => {
// see all possible matchers at
// https://sinonjs.org/releases/latest/matchers/
const calculator = {
/**
* returns the sum of two arguments
* @param a {number}
* @param b {number}
*/
add (a, b) {
return a + b
},
}
const spy = cy.spy(calculator, 'add').as('add')
expect(calculator.add(2, 3)).to.equal(5)
// if we want to assert the exact values used during the call
expect(spy).to.be.calledWith(2, 3)
// let's confirm "add" method was called with two numbers
expect(spy).to.be.calledWith(Cypress.sinon.match.number, Cypress.sinon.match.number)
// alternatively, provide the value to match
expect(spy).to.be.calledWith(Cypress.sinon.match(2), Cypress.sinon.match(3))
// match any value
expect(spy).to.be.calledWith(Cypress.sinon.match.any, 3)
// match any value from a list
expect(spy).to.be.calledWith(Cypress.sinon.match.in([1, 2, 3]), 3)
/**
* Returns true if the given number is even
* @param {number} x
*/
const isEven = (x) => x % 2 === 0
// expect the value to pass a custom predicate function
// the second argument to "sinon.match(predicate, message)" is
// shown if the predicate does not pass and assertion fails
expect(spy).to.be.calledWith(Cypress.sinon.match(isEven, 'isEven'), 3)
/**
* Returns a function that checks if a given number is larger than the limit
* @param {number} limit
* @returns {(x: number) => boolean}
*/
const isGreaterThan = (limit) => (x) => x > limit
/**
* Returns a function that checks if a given number is less than the limit
* @param {number} limit
* @returns {(x: number) => boolean}
*/
const isLessThan = (limit) => (x) => x < limit
// you can combine several matchers using "and", "or"
expect(spy).to.be.calledWith(
Cypress.sinon.match.number,
Cypress.sinon.match(isGreaterThan(2), '> 2').and(Cypress.sinon.match(isLessThan(4), '< 4')),
)
expect(spy).to.be.calledWith(
Cypress.sinon.match.number,
Cypress.sinon.match(isGreaterThan(200), '> 200').or(Cypress.sinon.match(3)),
)
// matchers can be used from BDD assertions
cy.get('@add').should('have.been.calledWith',
Cypress.sinon.match.number, Cypress.sinon.match(3))
// you can alias matchers for shorter test code
const { match: M } = Cypress.sinon
cy.get('@add').should('have.been.calledWith', M.number, M(3))
})
})
/// <reference types="cypress" />
context('Local Storage / Session Storage', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/storage')
})
// Although localStorage is automatically cleared
// in between tests to maintain a clean state
// sometimes we need to clear localStorage manually
it('cy.clearLocalStorage() - clear all data in localStorage for the current origin', () => {
// https://on.cypress.io/clearlocalstorage
cy.get('.ls-btn').click()
cy.get('.ls-btn').should(() => {
expect(localStorage.getItem('prop1')).to.eq('red')
expect(localStorage.getItem('prop2')).to.eq('blue')
expect(localStorage.getItem('prop3')).to.eq('magenta')
})
cy.clearLocalStorage()
cy.getAllLocalStorage().should(() => {
expect(localStorage.getItem('prop1')).to.be.null
expect(localStorage.getItem('prop2')).to.be.null
expect(localStorage.getItem('prop3')).to.be.null
})
cy.get('.ls-btn').click()
cy.get('.ls-btn').should(() => {
expect(localStorage.getItem('prop1')).to.eq('red')
expect(localStorage.getItem('prop2')).to.eq('blue')
expect(localStorage.getItem('prop3')).to.eq('magenta')
})
// Clear key matching string in localStorage
cy.clearLocalStorage('prop1')
cy.getAllLocalStorage().should(() => {
expect(localStorage.getItem('prop1')).to.be.null
expect(localStorage.getItem('prop2')).to.eq('blue')
expect(localStorage.getItem('prop3')).to.eq('magenta')
})
cy.get('.ls-btn').click()
cy.get('.ls-btn').should(() => {
expect(localStorage.getItem('prop1')).to.eq('red')
expect(localStorage.getItem('prop2')).to.eq('blue')
expect(localStorage.getItem('prop3')).to.eq('magenta')
})
// Clear keys matching regex in localStorage
cy.clearLocalStorage(/prop1|2/)
cy.getAllLocalStorage().should(() => {
expect(localStorage.getItem('prop1')).to.be.null
expect(localStorage.getItem('prop2')).to.be.null
expect(localStorage.getItem('prop3')).to.eq('magenta')
})
})
it('cy.getAllLocalStorage() - get all data in localStorage for all origins', () => {
// https://on.cypress.io/getalllocalstorage
cy.get('.ls-btn').click()
// getAllLocalStorage() yields a map of origins to localStorage values
cy.getAllLocalStorage().should((storageMap) => {
expect(storageMap).to.deep.equal({
// other origins will also be present if localStorage is set on them
'https://example.cypress.io': {
'prop1': 'red',
'prop2': 'blue',
'prop3': 'magenta',
},
})
})
})
it('cy.clearAllLocalStorage() - clear all data in localStorage for all origins', () => {
// https://on.cypress.io/clearalllocalstorage
cy.get('.ls-btn').click()
// clearAllLocalStorage() yields null
cy.clearAllLocalStorage()
cy.getAllLocalStorage().should(() => {
expect(localStorage.getItem('prop1')).to.be.null
expect(localStorage.getItem('prop2')).to.be.null
expect(localStorage.getItem('prop3')).to.be.null
})
})
it('cy.getAllSessionStorage() - get all data in sessionStorage for all origins', () => {
// https://on.cypress.io/getallsessionstorage
cy.get('.ls-btn').click()
// getAllSessionStorage() yields a map of origins to sessionStorage values
cy.getAllSessionStorage().should((storageMap) => {
expect(storageMap).to.deep.equal({
// other origins will also be present if sessionStorage is set on them
'https://example.cypress.io': {
'prop4': 'cyan',
'prop5': 'yellow',
'prop6': 'black',
},
})
})
})
it('cy.clearAllSessionStorage() - clear all data in sessionStorage for all origins', () => {
// https://on.cypress.io/clearallsessionstorage
cy.get('.ls-btn').click()
// clearAllSessionStorage() yields null
cy.clearAllSessionStorage()
cy.getAllSessionStorage().should(() => {
expect(sessionStorage.getItem('prop4')).to.be.null
expect(sessionStorage.getItem('prop5')).to.be.null
expect(sessionStorage.getItem('prop6')).to.be.null
})
})
})
/// <reference types="cypress" />
context('Traversal', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/traversal')
})
it('.children() - get child DOM elements', () => {
// https://on.cypress.io/children
cy.get('.traversal-breadcrumb')
.children('.active')
.should('contain', 'Data')
})
it('.closest() - get closest ancestor DOM element', () => {
// https://on.cypress.io/closest
cy.get('.traversal-badge')
.closest('ul')
.should('have.class', 'list-group')
})
it('.eq() - get a DOM element at a specific index', () => {
// https://on.cypress.io/eq
cy.get('.traversal-list>li')
.eq(1).should('contain', 'siamese')
})
it('.filter() - get DOM elements that match the selector', () => {
// https://on.cypress.io/filter
cy.get('.traversal-nav>li')
.filter('.active').should('contain', 'About')
})
it('.find() - get descendant DOM elements of the selector', () => {
// https://on.cypress.io/find
cy.get('.traversal-pagination')
.find('li').find('a')
.should('have.length', 7)
})
it('.first() - get first DOM element', () => {
// https://on.cypress.io/first
cy.get('.traversal-table td')
.first().should('contain', '1')
})
it('.last() - get last DOM element', () => {
// https://on.cypress.io/last
cy.get('.traversal-buttons .btn')
.last().should('contain', 'Submit')
})
it('.next() - get next sibling DOM element', () => {
// https://on.cypress.io/next
cy.get('.traversal-ul')
.contains('apples').next().should('contain', 'oranges')
})
it('.nextAll() - get all next sibling DOM elements', () => {
// https://on.cypress.io/nextall
cy.get('.traversal-next-all')
.contains('oranges')
.nextAll().should('have.length', 3)
})
it('.nextUntil() - get next sibling DOM elements until next el', () => {
// https://on.cypress.io/nextuntil
cy.get('#veggies')
.nextUntil('#nuts').should('have.length', 3)
})
it('.not() - remove DOM elements from set of DOM elements', () => {
// https://on.cypress.io/not
cy.get('.traversal-disabled .btn')
.not('[disabled]').should('not.contain', 'Disabled')
})
it('.parent() - get parent DOM element from DOM elements', () => {
// https://on.cypress.io/parent
cy.get('.traversal-mark')
.parent().should('contain', 'Morbi leo risus')
})
it('.parents() - get parent DOM elements from DOM elements', () => {
// https://on.cypress.io/parents
cy.get('.traversal-cite')
.parents().should('match', 'blockquote')
})
it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => {
// https://on.cypress.io/parentsuntil
cy.get('.clothes-nav')
.find('.active')
.parentsUntil('.clothes-nav')
.should('have.length', 2)
})
it('.prev() - get previous sibling DOM element', () => {
// https://on.cypress.io/prev
cy.get('.birds').find('.active')
.prev().should('contain', 'Lorikeets')
})
it('.prevAll() - get all previous sibling DOM elements', () => {
// https://on.cypress.io/prevall
cy.get('.fruits-list').find('.third')
.prevAll().should('have.length', 2)
})
it('.prevUntil() - get all previous sibling DOM elements until el', () => {
// https://on.cypress.io/prevuntil
cy.get('.foods-list').find('#nuts')
.prevUntil('#veggies').should('have.length', 3)
})
it('.siblings() - get all sibling DOM elements', () => {
// https://on.cypress.io/siblings
cy.get('.traversal-pills .active')
.siblings().should('have.length', 2)
})
})
/// <reference types="cypress" />
context('Utilities', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/utilities')
})
it('Cypress._ - call a lodash method', () => {
// https://on.cypress.io/_
cy.request('https://jsonplaceholder.cypress.io/users')
.then((response) => {
let ids = Cypress._.chain(response.body).map('id').take(3).value()
expect(ids).to.deep.eq([1, 2, 3])
})
})
it('Cypress.$ - call a jQuery method', () => {
// https://on.cypress.io/$
let $li = Cypress.$('.utility-jquery li:first')
cy.wrap($li).should('not.have.class', 'active')
cy.wrap($li).click()
cy.wrap($li).should('have.class', 'active')
})
it('Cypress.Blob - blob utilities and base64 string conversion', () => {
// https://on.cypress.io/blob
cy.get('.utility-blob').then(($div) => {
// https://github.com/nolanlawson/blob-util#imgSrcToDataURL
// get the dataUrl string for the javascript-logo
return Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous')
.then((dataUrl) => {
// create an <img> element and set its src to the dataUrl
let img = Cypress.$('<img />', { src: dataUrl })
// need to explicitly return cy here since we are initially returning
// the Cypress.Blob.imgSrcToDataURL promise to our test
// append the image
$div.append(img)
cy.get('.utility-blob img').click()
cy.get('.utility-blob img').should('have.attr', 'src', dataUrl)
})
})
})
it('Cypress.minimatch - test out glob patterns against strings', () => {
// https://on.cypress.io/minimatch
let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', {
matchBase: true,
})
expect(matching, 'matching wildcard').to.be.true
matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', {
matchBase: true,
})
expect(matching, 'comments').to.be.false
// ** matches against all downstream path segments
matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', {
matchBase: true,
})
expect(matching, 'comments').to.be.true
// whereas * matches only the next path segment
matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', {
matchBase: false,
})
expect(matching, 'comments').to.be.false
})
it('Cypress.Promise - instantiate a bluebird promise', () => {
// https://on.cypress.io/promise
let waited = false
/**
* @return Bluebird<string>
*/
function waitOneSecond () {
// return a promise that resolves after 1 second
return new Cypress.Promise((resolve, reject) => {
setTimeout(() => {
// set waited to true
waited = true
// resolve with 'foo' string
resolve('foo')
}, 1000)
})
}
cy.then(() => {
// return a promise to cy.then() that
// is awaited until it resolves
return waitOneSecond().then((str) => {
expect(str).to.eq('foo')
expect(waited).to.be.true
})
})
})
})
/// <reference types="cypress" />
context('Viewport', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/viewport')
})
it('cy.viewport() - set the viewport size and dimension', () => {
// https://on.cypress.io/viewport
cy.get('#navbar').should('be.visible')
cy.viewport(320, 480)
// the navbar should have collapse since our screen is smaller
cy.get('#navbar').should('not.be.visible')
cy.get('.navbar-toggle').should('be.visible').click()
cy.get('.nav').find('a').should('be.visible')
// lets see what our app looks like on a super large screen
cy.viewport(2999, 2999)
// cy.viewport() accepts a set of preset sizes
// to easily set the screen to a device's width and height
// We added a cy.wait() between each viewport change so you can see
// the change otherwise it is a little too fast to see :)
cy.viewport('macbook-15')
cy.wait(200)
cy.viewport('macbook-13')
cy.wait(200)
cy.viewport('macbook-11')
cy.wait(200)
cy.viewport('ipad-2')
cy.wait(200)
cy.viewport('ipad-mini')
cy.wait(200)
cy.viewport('iphone-6+')
cy.wait(200)
cy.viewport('iphone-6')
cy.wait(200)
cy.viewport('iphone-5')
cy.wait(200)
cy.viewport('iphone-4')
cy.wait(200)
cy.viewport('iphone-3')
cy.wait(200)
// cy.viewport() accepts an orientation for all presets
// the default orientation is 'portrait'
cy.viewport('ipad-2', 'portrait')
cy.wait(200)
cy.viewport('iphone-4', 'landscape')
cy.wait(200)
// The viewport will be reset back to the default dimensions
// in between tests (the default can be set in cypress.config.{js|ts})
})
})
/// <reference types="cypress" />
context('Waiting', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/waiting')
})
// BE CAREFUL of adding unnecessary wait times.
// https://on.cypress.io/best-practices#Unnecessary-Waiting
// https://on.cypress.io/wait
it('cy.wait() - wait for a specific amount of time', () => {
cy.get('.wait-input1').type('Wait 1000ms after typing')
cy.wait(1000)
cy.get('.wait-input2').type('Wait 1000ms after typing')
cy.wait(1000)
cy.get('.wait-input3').type('Wait 1000ms after typing')
cy.wait(1000)
})
it('cy.wait() - wait for a specific route', () => {
// Listen to GET to comments/1
cy.intercept('GET', '**/comments/*').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.network-btn').click()
// wait for GET comments/1
cy.wait('@getComment').its('response.statusCode').should('be.oneOf', [200, 304])
})
})
/// <reference types="cypress" />
context('Window', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/window')
})
it('cy.window() - get the global window object', () => {
// https://on.cypress.io/window
cy.window().should('have.property', 'top')
})
it('cy.document() - get the document object', () => {
// https://on.cypress.io/document
cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
})
it('cy.title() - get the title', () => {
// https://on.cypress.io/title
cy.title().should('include', 'Kitchen Sink')
})
})
# CollectorList 组件 Cypress 测试说明
## 概述
本项目为 CollectorList(布袋周期管理)组件提供了完整的 Cypress 端到端测试覆盖,包括基础功能测试、高级功能测试和简化测试。
## 测试文件结构
```
cypress/e2e/
├── collector-list.cy.js # 基础功能测试
├── collector-list-advanced.cy.js # 高级功能测试(包含API模拟)
├── collector-list-simple.cy.js # 简化测试(使用自定义命令)
└── README-collector-list.md # 本说明文档
```
## 测试覆盖范围
### 1. 基础功能测试 (`collector-list.cy.js`)
**测试场景:**
- 页面组件显示验证
- 搜索表单字段验证
- 搜索功能测试
- 重置功能测试
- 日期选择器测试
- 表格数据显示
- 分析对话框功能
- 分页功能验证
- 表格列标题验证
- 空数据处理
- 响应式设计验证
- 输入验证
- 表格交互性
- 页面性能测试
- 错误处理
- 数据刷新功能
### 2. 高级功能测试 (`collector-list-advanced.cy.js`)
**测试场景:**
- API 模拟和数据验证
- 精确搜索功能
- 分页数据处理
- 对话框完整操作流程
- 日期范围搜索
- 空搜索结果处理
- API 错误处理
- 表格排序功能
- 数据完整性验证
- 性能指标验证
- 输入验证边界测试
- 响应式布局测试
### 3. 简化测试 (`collector-list-simple.cy.js`)
**测试场景:**
- 使用自定义命令的基础功能测试
- 页面组件验证
- 搜索和重置功能
- 对话框操作
- 表格验证
- 响应式设计
- 性能测试
## 自定义命令
`cypress/support/commands.js` 中添加了以下专门用于 CollectorList 测试的自定义命令:
### 导航命令
- `navigateToCollectorList()` - 导航到布袋周期页面
### 搜索命令
- `searchCollectorData(compart, dusterName, dateRange)` - 搜索布袋周期数据
- `resetCollectorSearch()` - 重置搜索条件
### 对话框命令
- `openAnalysisDialog(method)` - 打开更换周期分析对话框
- `closeAnalysisDialog()` - 关闭分析对话框
### 验证命令
- `verifyCollectorTableData(expectedData)` - 验证表格数据完整性
- `verifyCollectorTableHeaders()` - 验证表格列标题
### API 模拟命令
- `mockCollectorAPI(apiType, responseData)` - 模拟 API 响应
### 工具命令
- `waitForPageLoad()` - 等待页面加载完成
## 测试数据
`cypress/fixtures/testData.json` 中添加了专门的测试数据:
### collectorListData
包含布袋周期列表的模拟数据,包括:
- 除尘器名称
- 仓室信息
- 布袋位置
- 更换时间
- 更换人
- 更换周期
### dustAnalysisData
包含分析相关的模拟数据,包括:
- 除尘器列表
- 分析图表数据
## 运行测试
### 运行所有 CollectorList 测试
```bash
npx cypress run --spec "cypress/e2e/collector-list*.cy.js"
```
### 运行特定测试文件
```bash
# 基础功能测试
npx cypress run --spec "cypress/e2e/collector-list.cy.js"
# 高级功能测试
npx cypress run --spec "cypress/e2e/collector-list-advanced.cy.js"
# 简化测试
npx cypress run --spec "cypress/e2e/collector-list-simple.cy.js"
```
### 在浏览器中运行测试
```bash
npx cypress open
```
然后在 Cypress 测试运行器中选择相应的测试文件。
## 测试环境要求
1. **登录状态模拟** - 测试使用 `mockLogin()` 命令模拟用户登录状态
2. **API 模拟** - 高级测试使用 `cy.intercept()` 模拟 API 响应
3. **测试数据** - 使用 `cy.fixture()` 加载测试数据
## 注意事项
1. **data-testid 属性** - 确保 CollectorList 组件中的元素包含正确的 `data-testid` 属性
2. **API 路径** - 测试中的 API 路径需要与实际后端 API 路径匹配
3. **日期选择器** - 日期选择器的测试可能需要根据实际的 Element UI 实现进行调整
4. **响应式测试** - 响应式测试在不同浏览器中可能有不同的表现
## 维护和扩展
### 添加新的测试场景
1. 在相应的测试文件中添加新的 `it()` 测试用例
2. 如果需要,在 `commands.js` 中添加新的自定义命令
3.`testData.json` 中添加相应的测试数据
### 修改现有测试
1. 确保修改后的测试仍然覆盖原有的功能
2. 更新相关的自定义命令和测试数据
3. 验证测试在不同环境下的稳定性
### 调试测试
1. 使用 `cy.debug()``cy.pause()` 进行调试
2. 查看 Cypress 测试运行器的实时日志
3. 使用 `cy.screenshot()` 捕获测试失败时的截图
## 最佳实践
1. **测试隔离** - 每个测试用例应该是独立的,不依赖其他测试的状态
2. **数据清理** - 在测试完成后清理测试数据
3. **错误处理** - 测试应该能够处理各种错误情况
4. **性能考虑** - 避免不必要的等待时间,使用适当的断言
5. **可读性** - 使用描述性的测试名称和清晰的测试结构
/// <reference types="cypress" />
describe('告警总览功能测试', () => {
beforeEach(() => {
// 模拟登录状态
cy.mockLogin()
// 等待登录状态设置完成
cy.wait(500)
// 访问告警总览页面
cy.visit('/#/alerts')
// 等待页面加载完成
cy.waitForPageLoad()
})
it('应该显示告警总览页面的所有核心组件', () => {
// 检查主容器
cy.get('.page-container').should('be.visible')
// 检查内容区域
cy.get('.content-box').should('be.visible')
// 检查搜索表单
cy.get('.search').should('be.visible')
cy.get('.demo-form-inline').should('be.visible')
// 检查表格区域
cy.get('.table-box').should('be.visible')
})
it('应该显示所有搜索条件输入框', () => {
// 检查事件名称输入框
cy.get('input[placeholder="请输入事件名称"]').should('be.visible')
// 检查发生位置输入框
cy.get('input[placeholder="请输入发生位置"]').should('be.visible')
// 检查除尘器名称输入框
cy.get('input[placeholder="请输入除尘器名称"]').should('be.visible')
// 检查设备类型选择框
cy.get('.el-select').should('be.visible')
// 检查告警时间选择器
cy.get('input[placeholder="开始时间"]').should('be.visible')
})
it('应该能够输入搜索条件', () => {
// 输入事件名称
cy.get('input[placeholder="请输入事件名称"]').clear().type('测试事件')
cy.get('input[placeholder="请输入事件名称"]').should('have.value', '测试事件')
// 输入发生位置
cy.get('input[placeholder="请输入发生位置"]').clear().type('测试位置')
cy.get('input[placeholder="请输入发生位置"]').should('have.value', '测试位置')
// 输入除尘器名称
cy.get('input[placeholder="请输入除尘器名称"]').clear().type('测试除尘器')
cy.get('input[placeholder="请输入除尘器名称"]').should('have.value', '测试除尘器')
})
it('应该能够选择设备类型', () => {
// 点击设备类型选择框 - 使用更精确的选择器
cy.get('.el-form-item:contains("设备类型") .el-select').first().click()
// 等待下拉选项出现
cy.get('.el-select-dropdown').should('be.visible')
// 如果有选项,选择第一个
cy.get('.el-select-dropdown').then(($dropdown) => {
if ($dropdown.find('.el-select-dropdown__item').length > 0) {
cy.get('.el-select-dropdown__item').first().click()
}
})
})
it('应该能够设置告警时间范围', () => {
// 点击时间选择器 - 使用更通用的选择器
cy.get('input[placeholder="开始时间"]').first().click()
// 等待日期面板出现
cy.get('.el-picker-panel').should('be.visible')
// 选择开始日期 - 使用更精确的选择器
cy.get('.el-picker-panel .el-date-table td.available').first().click()
// 选择结束日期 - 使用更精确的选择器
cy.get('.el-picker-panel .el-date-table td.available').eq(5).click()
// 点击确定按钮
cy.get('.el-picker-panel__footer .el-button--primary').click()
// 验证时间选择器有值
cy.get('input[placeholder="开始时间"]').should('not.have.value', '')
})
it('应该能够重置搜索条件', () => {
// 先输入一些搜索条件
cy.get('input[placeholder="请输入事件名称"]').clear().type('测试事件')
cy.get('input[placeholder="请输入发生位置"]').clear().type('测试位置')
// 点击重置按钮
cy.get('.reset-btn-balck-theme').click()
// 验证输入框已清空
cy.get('input[placeholder="请输入事件名称"]').should('have.value', '')
cy.get('input[placeholder="请输入发生位置"]').should('have.value', '')
})
it('应该能够执行搜索查询', () => {
// 输入搜索条件
cy.get('input[placeholder="请输入事件名称"]').clear().type('测试事件')
// 点击查询按钮
cy.get('.search-btn-balck-theme').click()
// 等待搜索结果加载
cy.wait(1000)
// 验证页面仍然正常显示
cy.get('.table-box').should('be.visible')
})
it('应该显示挂起设备按钮', () => {
// 检查挂起设备按钮
cy.get('button').contains('挂起设备').should('be.visible')
})
it('应该能够选择告警类型', () => {
// 检查单选按钮组
cy.get('.el-radio-group').should('be.visible')
// 检查各个选项
cy.get('.el-radio').contains('挂起期间告警').should('be.visible')
cy.get('.el-radio').contains('非挂起期间告警').should('be.visible')
cy.get('.el-radio').contains('全部告警').should('be.visible')
// 选择不同的选项
cy.get('.el-radio').contains('挂起期间告警').click()
cy.get('.el-radio').contains('非挂起期间告警').click()
cy.get('.el-radio').contains('全部告警').click()
})
it('应该显示告警数据表格', () => {
// 检查表格容器
cy.get('.table-box').should('be.visible')
// 检查表格组件
cy.get('.el-table').should('be.visible')
// 检查表格头部
cy.get('.el-table__header').should('be.visible')
})
it('应该显示表格分页组件', () => {
// 检查分页组件
cy.get('.el-pagination').should('be.visible')
// 检查分页信息
cy.get('.el-pagination__total').should('be.visible')
})
it('应该能够进行分页操作', () => {
// 等待页面加载完成
cy.wait(1000)
// 检查是否有分页组件,如果有则进行分页操作测试
cy.get('body').then(($body) => {
if ($body.find('.el-pagination').length > 0) {
// 检查分页按钮
cy.get('.el-pagination__prev').should('be.visible')
cy.get('.el-pagination__next').should('be.visible')
// 检查页码按钮
cy.get('.el-pager').should('be.visible')
} else {
// 如果没有分页组件,记录日志并继续
cy.log('没有分页组件,数据量可能较少')
// 验证页面仍然正常显示
cy.get('.table-box').should('be.visible')
}
})
})
it('应该显示表格操作列', () => {
// 检查表格行
cy.get('.el-table__body tr').then(($rows) => {
if ($rows.length > 0) {
// 检查操作列
cy.get('.el-table__body tr').first().within(() => {
cy.get('td').last().should('contain', '暂挂起')
})
}
})
})
it('应该能够点击暂挂起操作', () => {
// 检查表格行
cy.get('.el-table__body tr').then(($rows) => {
if ($rows.length > 0) {
// 点击暂挂起按钮
cy.get('.el-table__body tr').first().within(() => {
cy.get('td').last().contains('暂挂起').click()
})
// 这里可以添加点击后的验证逻辑
// 比如检查是否有弹窗、确认对话框等
}
})
})
it('应该显示告警级别标识', () => {
// 等待表格数据加载
cy.wait(1000)
// 检查表格行
cy.get('.el-table__body tr').then(($rows) => {
if ($rows.length > 0) {
// 检查表格行是否有内容
cy.get('.el-table__body tr').first().within(() => {
// 检查是否有任何内容,不特定检查'level'
cy.get('td').should('have.length.greaterThan', 0)
})
} else {
// 如果没有数据行,记录日志
cy.log('表格中没有数据行')
}
})
})
it('应该处理表格数据加载', () => {
// 等待表格数据加载
cy.wait(1000)
// 验证表格仍然可见
cy.get('.el-table').should('be.visible')
// 验证表格有内容
cy.get('.el-table__body').should('be.visible')
})
it('应该响应搜索条件变化', () => {
// 输入搜索条件并查询
cy.get('input[placeholder="请输入事件名称"]').clear().type('测试事件')
cy.get('.search-btn-balck-theme').click()
// 等待搜索结果
cy.wait(1000)
// 验证页面正常显示
cy.get('.table-box').should('be.visible')
// 清空搜索条件并查询
cy.get('.reset-btn-balck-theme').click()
cy.get('.search-btn-balck-theme').click()
// 等待结果
cy.wait(1000)
// 验证页面正常显示
cy.get('.table-box').should('be.visible')
})
it('应该检查响应式设计', () => {
// 检查不同屏幕尺寸下的显示
const viewports = [
{ width: 1920, height: 1080 }, // 桌面
{ width: 1280, height: 720 }, // 笔记本
{ width: 768, height: 1024 }, // 平板
]
viewports.forEach(viewport => {
cy.viewport(viewport.width, viewport.height)
cy.wait(500)
// 验证主要组件仍然可见
cy.get('.page-container').should('be.visible')
cy.get('.search').should('be.visible')
cy.get('.table-box').should('be.visible')
})
})
it('应该处理空数据状态', () => {
// 输入一个不存在的搜索条件
cy.get('input[placeholder="请输入事件名称"]').clear().type('不存在的告警事件')
cy.get('.search-btn-balck-theme').click()
// 等待搜索结果
cy.wait(1000)
// 验证页面仍然正常显示
cy.get('.table-box').should('be.visible')
})
it('应该验证表格列标题', () => {
// 检查表格头部
cy.get('.el-table__header').should('be.visible')
// 检查表格头部行
cy.get('.el-table__header tr').should('be.visible')
// 检查表格头部单元格
cy.get('.el-table__header th').should('be.visible')
})
})
This diff is collapsed.
/// <reference types="cypress" />
describe('布袋周期管理简化测试', () => {
beforeEach(() => {
// 模拟登录状态
cy.mockLogin()
// 导航到布袋周期页面
cy.navigateToCollectorList()
})
it('应该正确显示页面组件', () => {
// 验证页面组件
cy.get('[data-testid="collector-list-container"]').should('be.visible')
cy.get('[data-testid="collector-list-search-form"]').should('be.visible')
cy.get('[data-testid="collector-table-container"]').should('be.visible')
cy.get('[data-testid="collector-common-table"]').should('be.visible')
})
it('应该验证搜索表单字段', () => {
// 验证搜索表单字段
cy.get('[data-testid="collector-compart-input"]').should('be.visible')
cy.get('[data-testid="collector-duster-name-input"]').should('be.visible')
cy.get('[data-testid="collector-date-picker"]').should('be.visible')
cy.get('[data-testid="collector-reset-button"]').should('be.visible')
cy.get('[data-testid="collector-search-button"]').should('be.visible')
cy.get('[data-testid="collector-analysis-button"]').should('be.visible')
})
it('应该能够进行搜索操作', () => {
// 使用自定义命令进行搜索
cy.searchCollectorData('测试仓室', '测试除尘器')
// 验证表格仍然可见
cy.get('[data-testid="collector-common-table"]').should('be.visible')
})
it('应该能够重置搜索条件', () => {
// 先输入一些搜索条件
cy.get('[data-testid="collector-compart-input"]').type('测试数据')
cy.get('[data-testid="collector-duster-name-input"]').type('测试数据')
// 使用自定义命令重置
cy.resetCollectorSearch()
// 验证表格仍然可见
cy.get('[data-testid="collector-common-table"]').should('be.visible')
})
it('应该能够打开分析对话框', () => {
// 使用自定义命令打开对话框
cy.openAnalysisDialog('button')
// 验证对话框内容
cy.get('.dustListDialog').within(() => {
cy.get('.input-group').should('be.visible')
cy.get('.el-select').should('be.visible')
cy.get('.echartBox').should('be.visible')
})
// 关闭对话框
cy.closeAnalysisDialog()
})
it('应该能够通过双击除尘器名称打开对话框', () => {
// 使用自定义命令通过双击打开对话框
cy.openAnalysisDialog('dblclick')
// 关闭对话框
cy.closeAnalysisDialog()
})
it('应该验证表格列标题', () => {
// 使用自定义命令验证表格列标题
cy.verifyCollectorTableHeaders()
})
it('应该验证页面响应式设计', () => {
// 使用现有的响应式检查命令
cy.checkResponsive()
})
it('应该验证除尘器名称链接样式', () => {
// 验证除尘器名称链接的样式
cy.get('[data-testid="collector-duster-name-link"]').first().should('have.class', 'health-score')
cy.get('[data-testid="collector-duster-name-link"]').first().should('have.class', 'green-color')
})
it('应该验证页面加载性能', () => {
// 验证页面加载性能
cy.window().then((win) => {
const performance = win.performance
const navigation = performance.getEntriesByType('navigation')[0]
// 验证页面加载时间在合理范围内
expect(navigation.loadEventEnd - navigation.loadEventStart).to.be.lessThan(10000)
})
})
})
This diff is collapsed.
This diff is collapsed.
/// <reference types="cypress" />
describe('仪表板功能测试', () => {
beforeEach(() => {
// 模拟登录状态
cy.mockLogin()
// 等待登录状态设置完成
cy.wait(500)
// 访问仪表板
cy.visit('/#/dashboard')
// 等待页面加载完成
cy.waitForPageLoad()
})
it('应该显示仪表板的所有核心组件', () => {
// 检查主容器
cy.get('[data-testid="dashboard-container"]').should('be.visible')
// 检查头部区域
cy.get('[data-testid="dashboard-header"]').should('be.visible')
// 检查消息框
cy.get('[data-testid="dashboard-msg-box"]').should('be.visible')
cy.get('[data-testid="dashboard-msg-item"]').should('be.visible')
// 检查指标框
cy.get('[data-testid="dashboard-indicators-box"]').should('be.visible')
cy.get('[data-testid="dashboard-health-score"]').should('be.visible')
// 检查图表区域
cy.get('[data-testid="dashboard-chart-box"]').should('be.visible')
cy.get('[data-testid="dashboard-chart-line"]').should('be.visible')
// 检查地图区域
cy.get('[data-testid="dashboard-map-box"]').should('be.visible')
cy.get('[data-testid="dashboard-map-svg"]').should('be.visible')
})
it('应该显示健康度指标', () => {
cy.verifyHealthIndicators()
// 检查健康度数值格式
cy.get('[data-testid="dashboard-health-score"]')
.should('be.visible')
.and('contain', '%')
// 检查进度条
cy.get('[data-testid="dashboard-bag-progress"]').should('be.visible')
cy.get('[data-testid="dashboard-pulse-valve-progress"]').should('be.visible')
cy.get('[data-testid="dashboard-poppet-valve-progress"]').should('be.visible')
})
it('应该加载并显示图表数据', () => {
// 等待图表组件加载
cy.get('[data-testid="dashboard-chart-line"]').should('be.visible')
// 检查图表是否有内容(这里需要根据实际图表实现调整)
cy.get('[data-testid="dashboard-chart-line"]').within(() => {
// 检查图表容器内是否有内容
cy.get('*').should('have.length.gt', 0)
})
})
it('应该显示地图组件', () => {
// 检查地图组件
cy.get('[data-testid="dashboard-map-svg"]').should('be.visible')
// 检查地图是否有内容
cy.get('[data-testid="dashboard-map-svg"]').within(() => {
cy.get('*').should('have.length.gt', 0)
})
})
it('应该响应数据更新', () => {
// 记录初始健康度值
cy.get('[data-testid="dashboard-health-score"]').then(($el) => {
const initialValue = $el.text()
// 等待一段时间,检查数据是否可能更新
cy.wait(2000)
// 验证元素仍然存在并且可能有更新
cy.get('[data-testid="dashboard-health-score"]').should('be.visible')
})
})
it('应该处理消息列表', () => {
// 检查消息组件
cy.get('[data-testid="dashboard-msg-item"]').should('be.visible')
// 如果有消息项,检查其结构
cy.get('[data-testid="dashboard-msg-item"]').within(() => {
// 检查是否有消息内容
cy.get('*').should('have.length.gte', 0)
})
})
it('应该验证布局响应式', () => {
// 测试不同屏幕尺寸下的布局
const viewports = [
{ width: 1920, height: 1080 },
{ width: 1280, height: 720 },
{ width: 1024, height: 768 }
]
viewports.forEach(viewport => {
cy.viewport(viewport.width, viewport.height)
cy.wait(500)
// 验证主要组件在不同尺寸下仍然可见
cy.get('[data-testid="dashboard-container"]').should('be.visible')
cy.get('[data-testid="dashboard-header"]').should('be.visible')
cy.get('[data-testid="dashboard-map-box"]').should('be.visible')
})
})
it('应该验证颜色主题', () => {
// 检查健康度指标的颜色是否根据数值变化
cy.get('[data-testid="dashboard-health-score"]').then(($el) => {
const healthScore = parseInt($el.text().replace('%', ''))
// 根据健康度检查颜色
if (healthScore >= 90) {
cy.get('[data-testid="dashboard-health-score"]')
.should('have.css', 'color')
.and('not.equal', 'rgba(0, 0, 0, 0)')
}
})
})
it('应该处理数据加载状态', () => {
// 重新加载页面检查加载状态
cy.reload()
// 等待页面完全加载
cy.waitForPageLoad()
// 验证所有关键元素都已加载
cy.get('[data-testid="dashboard-container"]').should('be.visible')
cy.get('[data-testid="dashboard-health-score"]').should('be.visible')
})
it('应该正确处理无权限用户的重定向', () => {
// 清除localStorage模拟无权限状态
cy.window().then((win) => {
win.localStorage.clear()
win.sessionStorage.clear()
// 清除所有cookie
cy.clearCookies()
})
// 尝试访问仪表板,应该被重定向到登录页
cy.visit('/#/dashboard')
cy.url().should('include', '/#/login')
})
})
\ No newline at end of file
This diff is collapsed.
/// <reference types="cypress" />
describe('除尘器概览功能测试', () => {
beforeEach(() => {
// 模拟登录状态
cy.mockLogin()
// 访问除尘器概览页面
cy.visit('/#/dust-overview')
})
it('应该显示概览页面的所有核心元素', () => {
// 检查主容器
cy.get('[data-testid="dust-overview-container"]').should('be.visible')
// 检查头部统计卡片
cy.get('[data-testid="dust-overview-header"]').should('be.visible')
cy.get('[data-testid="dust-leak-alarm-card"]').should('be.visible')
cy.get('[data-testid="dust-health-card"]').should('be.visible')
cy.get('[data-testid="dust-close-loop-card"]').should('be.visible')
// 检查统计数据
cy.get('[data-testid="dust-leak-alarm-count"]').should('be.visible')
cy.get('[data-testid="dust-health-percent"]').should('be.visible')
cy.get('[data-testid="dust-close-loop-count"]').should('be.visible')
})
it('应该显示搜索表单', () => {
// 检查搜索表单
cy.get('[data-testid="dust-search-form"]').should('be.visible')
// 检查搜索控件
cy.get('[data-testid="dust-production-line-select"]').should('be.visible')
cy.get('[data-testid="dust-device-name-input"]').should('be.visible')
cy.get('[data-testid="dust-reset-button"]').should('be.visible')
cy.get('[data-testid="dust-search-button"]').should('be.visible')
cy.get('[data-testid="dust-add-button"]').should('be.visible')
})
it('应该显示数据表格', () => {
// 检查表格容器
cy.get('[data-testid="dust-table-container"]').should('be.visible')
cy.get('[data-testid="dust-table"]').should('be.visible')
// 检查表格是否已加载
cy.checkTableDataLoaded('[data-testid="dust-table"]')
})
it('应该能够执行搜索功能', () => {
// 使用设备名称搜索
cy.get('[data-testid="dust-device-name-input"]').clear().type('测试设备')
cy.get('[data-testid="dust-search-button"]').click()
// 等待搜索结果
cy.wait(1000)
// 验证表格仍然可见
cy.get('[data-testid="dust-table"]').should('be.visible')
})
it('应该能够使用工序筛选', () => {
// 点击工序选择框
cy.get('[data-testid="dust-production-line-select"]').click()
// 选择第一个选项(除了"全部")
cy.get('.el-select-dropdown__item').then($items => {
if ($items.length > 1) {
cy.wrap($items[1]).click()
// 点击搜索
cy.get('[data-testid="dust-search-button"]').click()
cy.wait(1000)
}
})
})
it('应该能够重置搜索条件', () => {
// 填写搜索条件
cy.get('[data-testid="dust-device-name-input"]').type('测试')
// 点击重置按钮
cy.get('[data-testid="dust-reset-button"]').click()
// 验证输入框已清空
cy.get('[data-testid="dust-device-name-input"]').should('have.value', '')
})
it('应该能够点击统计卡片进行导航', () => {
// 点击泄漏告警卡片
cy.get('[data-testid="dust-leak-alarm-card"]').click()
// 验证URL变化或者页面响应
cy.url().should('not.contain', '/dust-overview')
// 返回概览页面继续测试
cy.go('back')
cy.get('[data-testid="dust-overview-container"]').should('be.visible')
// 点击闭环卡片
cy.get('[data-testid="dust-close-loop-card"]').click()
cy.url().should('not.contain', '/dust-overview')
cy.go('back')
})
it('应该能够点击表格中的操作按钮', () => {
// 等待表格数据加载
cy.checkTableDataLoaded('[data-testid="dust-table"]')
// 检查是否有数据行
cy.get('[data-testid="dust-table"] tbody tr').then($rows => {
if ($rows.length > 0) {
// 点击第一行的查看按钮
cy.get('[data-testid="dust-view-button"]').first().click()
// 验证跳转或弹窗
cy.wait(1000)
// 如果是页面跳转,返回
cy.url().then(url => {
if (!url.includes('/dust-overview')) {
cy.go('back')
}
})
// 点击编辑按钮
cy.get('[data-testid="dust-edit-button"]').first().click()
cy.wait(1000)
}
})
})
it('应该能够点击仓室数量和电磁阀数量链接', () => {
// 等待表格加载
cy.checkTableDataLoaded('[data-testid="dust-table"]')
// 检查仓室数量链接
cy.get('[data-testid="compartment-count-link"]').then($links => {
if ($links.length > 0) {
cy.wrap($links[0]).click()
cy.wait(1000)
}
})
// 检查电磁阀数量链接
cy.get('[data-testid="valve-count-link"]').then($links => {
if ($links.length > 0) {
cy.wrap($links[0]).click()
cy.wait(1000)
}
})
})
it('应该能够打开新增弹窗', () => {
// 点击新增按钮
cy.get('[data-testid="dust-add-button"]').click()
// 验证弹窗是否打开
cy.get('.el-dialog').should('be.visible')
// 关闭弹窗
cy.get('.el-dialog__close').click()
})
it('应该验证分页功能', () => {
// 检查分页组件
cy.get('[data-testid="pagination-container"]').should('be.visible')
cy.get('[data-testid="el-pagination"]').should('be.visible')
// 如果有多页数据,测试分页
cy.get('.el-pagination__total').then($total => {
const totalText = $total.text()
if (totalText && totalText.includes('条') && parseInt(totalText) > 20) {
// 点击下一页
cy.get('.btn-next').click()
cy.wait(1000)
// 点击上一页
cy.get('.btn-prev').click()
cy.wait(1000)
}
})
})
it('应该验证响应式设计', () => {
cy.checkResponsive()
// 验证在不同屏幕尺寸下表格和卡片的显示
cy.viewport(768, 1024)
cy.get('[data-testid="dust-overview-header"]').should('be.visible')
cy.get('[data-testid="dust-table-container"]').should('be.visible')
})
it('应该处理数据加载状态', () => {
// 重新加载页面
cy.reload()
cy.waitForPageLoad()
// 验证页面元素正常加载
cy.get('[data-testid="dust-overview-container"]').should('be.visible')
cy.get('[data-testid="dust-leak-alarm-count"]').should('be.visible')
cy.get('[data-testid="dust-health-percent"]').should('be.visible')
cy.get('[data-testid="dust-close-loop-count"]').should('be.visible')
})
})
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
describe('template spec', () => {
it('passes', () => {
cy.visit('https://example.cypress.io')
})
/* ==== Test Created with Cypress Studio ==== */
it('test1', function() {
/* ==== Generated with Cypress Studio ==== */
cy.visit('http://localhost:3000/#/login');
cy.get('[data-testid="login-username-input"]').clear('z');
cy.get('[data-testid="login-username-input"]').type('zongheng_admin');
cy.get('[data-testid="login-password-input"]').clear('9%#F46vt');
cy.get('[data-testid="login-password-input"]').type('9%#F46vt');
cy.get('[data-testid="login-captcha-input"]').clear('8');
cy.get('[data-testid="login-captcha-input"]').type('8888');
cy.get('[data-testid="login-submit-button"]').click();
cy.get('[data-testid="menu-item-collectorList"]').click();
cy.get('[data-testid="menu-item-monitor"] > span').click();
cy.get('[data-testid="dust-monitoring-status-matrix"] > .left > :nth-child(1) > :nth-child(1)').click();
cy.get(':nth-child(1) > span > .el-icon').click();
cy.get(':nth-child(1) > span > .el-icon').click();
/* ==== End Cypress Studio ==== */
});
})
\ No newline at end of file
describe('template spec', () => {
it('passes', () => {
cy.visit('https://example.cypress.io')
})
/* ==== Test Created with Cypress Studio ==== */
it('test4', function() {
/* ==== Generated with Cypress Studio ==== */
cy.visit('http://localhost:3000');
cy.get('[data-testid="login-username-input"]').click();
cy.get('[data-testid="login-username-input"]').clear('z');
cy.get('[data-testid="login-username-input"]').type('zongheng_admin');
cy.get('[data-testid="login-password-input"]').click();
cy.get('[data-testid="login-password-input"]').click();
cy.get('[data-testid="login-password-input"]').clear('9%#F46vt');
cy.get('[data-testid="login-password-input"]').type('9%#F46vt');
cy.get('[data-testid="login-captcha-input"]').clear('8');
cy.get('[data-testid="login-captcha-input"]').type('8888');
cy.get('[data-testid="login-submit-button"]').click();
cy.get('[data-testid="menu-item-dust-overview"] > span').click();
cy.get('[data-testid="menu-item-monitor"] > span').click();
cy.get('.el-date-editor > [placeholder="开始日期"]').click();
cy.get('.is-left > .el-date-table > tbody > :nth-child(5) > :nth-child(5) > .el-date-table-cell > .el-date-table-cell__text').click();
cy.get('.end-date > .el-date-table-cell > .el-date-table-cell__text').click();
cy.get('.is-plain > span').click();
cy.get('[data-testid="menu-item-collectorList"]').click();
cy.get('[data-testid="menu-item-monitor"] > span').click();
/* ==== End Cypress Studio ==== */
});
})
\ No newline at end of file
describe('template spec', () => {
it('passes', () => {
cy.visit('https://example.cypress.io')
})
/* ==== Test Created with Cypress Studio ==== */
it('test3', function() {
/* ==== Generated with Cypress Studio ==== */
cy.visit('http://localhost:3000/');
cy.get('[data-testid="login-username-input"]').click();
cy.get('[data-testid="login-username-input"]').click();
cy.get('[data-testid="login-username-input"]').clear('zo');
cy.get('[data-testid="login-username-input"]').type('zongheng_admin');
cy.get('[data-testid="login-password-input"]').click();
cy.get('[data-testid="login-password-input"]').clear('9%#F46vt ');
cy.get('[data-testid="login-password-input"]').type('9%#F46vt ');
cy.get('[data-testid="login-captcha-input"]').clear('8');
cy.get('[data-testid="login-captcha-input"]').type('8888');
cy.get('.el-form').click();
cy.get('.el-form').click();
cy.get('.el-form').click();
cy.get('[data-testid="login-submit-button"]').click();
cy.get('.el-input__suffix-inner > .el-icon > svg').click();
cy.get('[data-testid="login-password-input"]').clear();
cy.get('[data-testid="login-password-input"]').type('9%#F46vt');
cy.get('[data-testid="login-submit-button"] > span').click();
cy.get('[data-testid="menu-item-collectorList"] > span').click();
cy.get('[data-testid="menu-item-dust-overview"] > span').click();
cy.get('[data-testid="menu-item-collectorList"]').click();
cy.get('[data-testid="menu-item-monitor"]').click();
cy.get('.left > :nth-child(1) > :nth-child(11)').click();
cy.get(':nth-child(1) > span > .el-icon').click();
cy.get(':nth-child(2) > span > .el-icon').click();
cy.get(':nth-child(2) > :nth-child(6) > span').click();
cy.get('.el-select__suffix > .el-icon > svg').click();
cy.get('#el-id-3043-155 > span').click();
/* ==== End Cypress Studio ==== */
});
})
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<!doctype html>
<html lang="en"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>DC-TOM 测试报告</title><link rel="stylesheet" href="assets/app.css"/></head><body data-raw="{&quot;stats&quot;:{&quot;suites&quot;:1,&quot;tests&quot;:2,&quot;passes&quot;:1,&quot;pending&quot;:0,&quot;failures&quot;:1,&quot;start&quot;:&quot;2025-09-02T01:33:13.291Z&quot;,&quot;end&quot;:&quot;2025-09-02T01:33:21.444Z&quot;,&quot;duration&quot;:8153,&quot;testsRegistered&quot;:2,&quot;passPercent&quot;:50,&quot;pendingPercent&quot;:0,&quot;other&quot;:0,&quot;hasOther&quot;:false,&quot;skipped&quot;:0,&quot;hasSkipped&quot;:false},&quot;results&quot;:[{&quot;uuid&quot;:&quot;f18a73a3-f766-45eb-b8a9-54d327c8cd97&quot;,&quot;title&quot;:&quot;&quot;,&quot;fullFile&quot;:&quot;cypress/e2e/spec.cy.js&quot;,&quot;file&quot;:&quot;cypress/e2e/spec.cy.js&quot;,&quot;beforeHooks&quot;:[],&quot;afterHooks&quot;:[],&quot;tests&quot;:[],&quot;suites&quot;:[{&quot;uuid&quot;:&quot;b08d0fe5-99c2-4e84-8063-f358d2c8fc6e&quot;,&quot;title&quot;:&quot;template spec&quot;,&quot;fullFile&quot;:&quot;&quot;,&quot;file&quot;:&quot;&quot;,&quot;beforeHooks&quot;:[],&quot;afterHooks&quot;:[],&quot;tests&quot;:[{&quot;title&quot;:&quot;passes&quot;,&quot;fullTitle&quot;:&quot;template spec passes&quot;,&quot;duration&quot;:1552,&quot;state&quot;:&quot;passed&quot;,&quot;speed&quot;:&quot;fast&quot;,&quot;pass&quot;:true,&quot;fail&quot;:false,&quot;pending&quot;:false,&quot;code&quot;:&quot;cy.visit(&#x27;https://example.cypress.io&#x27;);&quot;,&quot;err&quot;:{},&quot;uuid&quot;:&quot;57a4f280-853c-47a8-91c7-b3d5daf05f24&quot;,&quot;parentUUID&quot;:&quot;b08d0fe5-99c2-4e84-8063-f358d2c8fc6e&quot;,&quot;isHook&quot;:false,&quot;skipped&quot;:false},{&quot;title&quot;:&quot;test1&quot;,&quot;fullTitle&quot;:&quot;template spec test1&quot;,&quot;duration&quot;:1204,&quot;state&quot;:&quot;failed&quot;,&quot;pass&quot;:false,&quot;fail&quot;:true,&quot;pending&quot;:false,&quot;code&quot;:&quot;/* ==== Generated with Cypress Studio ==== */\ncy.visit(&#x27;http://localhost:3000/&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-username-input\&quot;]&#x27;).clear(&#x27;z&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-username-input\&quot;]&#x27;).type(&#x27;zongheng_admin&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-password-input\&quot;]&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;login-password-input\&quot;]&#x27;).clear(&#x27;9%#F46vt&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-password-input\&quot;]&#x27;).type(&#x27;9%#F46vt&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-captcha-input\&quot;]&#x27;).clear(&#x27;8&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-captcha-input\&quot;]&#x27;).type(&#x27;8888&#x27;);\ncy.get(&#x27;.el-checkbox__label&#x27;).click();\ncy.get(&#x27;.el-checkbox__original&#x27;).check();\ncy.get(&#x27;[data-testid=\&quot;login-submit-button\&quot;]&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;menu-item-dust-overview\&quot;] &gt; span&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;dust-add-button\&quot;] &gt; span&#x27;).click();\ncy.get(&#x27;#el-id-7502-285&#x27;).click();\ncy.get(&#x27;#el-id-7502-97 &gt; span&#x27;).click();\ncy.get(&#x27;#el-id-7502-286&#x27;).clear(&#x27;1&#x27;);\ncy.get(&#x27;#el-id-7502-286&#x27;).type(&#x27;1&#x27;);\ncy.get(&#x27;#el-id-7502-287&#x27;).clear(&#x27;2&#x27;);\ncy.get(&#x27;#el-id-7502-287&#x27;).type(&#x27;2&#x27;);\ncy.get(&#x27;#el-id-7502-289&#x27;).click();\ncy.get(&#x27;.el-dialog__headerbtn &gt; .el-icon &gt; svg&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;dust-search-button\&quot;] &gt; span&#x27;).click();\ncy.get(&#x27;:nth-child(1) &gt; .el-table_1_column_10 &gt; .cell &gt; [data-testid=\&quot;dust-view-button\&quot;]&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;dust-monitoring-status-matrix\&quot;] &gt; .left &gt; :nth-child(1) &gt; :nth-child(1)&#x27;).click();\ncy.get(&#x27;.el-select__suffix &gt; .el-icon &gt; svg&#x27;).click();\ncy.get(&#x27;#el-id-7502-313 &gt; span&#x27;).click();\n/* ==== End Cypress Studio ==== */&quot;,&quot;err&quot;:{&quot;message&quot;:&quot;CypressError: `cy.check()` failed because this element is not visible:\n\n`&lt;input class=\&quot;el-checkbox__original\&quot; type=\&quot;checkbox\&quot; value=\&quot;记住密码\&quot;&gt;`\n\nThis element `&lt;input.el-checkbox__original&gt;` is not visible because it has an effective width and height of: `0 x 0` pixels.\n\nFix this problem, or use `{force: true}` to disable error checking.\n\nhttps://on.cypress.io/element-cannot-be-interacted-with&quot;,&quot;estack&quot;:&quot;CypressError: `cy.check()` failed because this element is not visible:\n\n`&lt;input class=\&quot;el-checkbox__original\&quot; type=\&quot;checkbox\&quot; value=\&quot;记住密码\&quot;&gt;`\n\nThis element `&lt;input.el-checkbox__original&gt;` is not visible because it has an effective width and height of: `0 x 0` pixels.\n\nFix this problem, or use `{force: true}` to disable error checking.\n\nhttps://on.cypress.io/element-cannot-be-interacted-with\n at runVisibilityCheck (http://localhost:3000/__cypress/runner/cypress_runner.js:144957:58)\n at Object.isVisible (http://localhost:3000/__cypress/runner/cypress_runner.js:144968:10)\n at checkOrUncheckEl (http://localhost:3000/__cypress/runner/cypress_runner.js:112342:24)\n at tryCatcher (http://localhost:3000/__cypress/runner/cypress_runner.js:1830:23)\n at Object.gotValue (http://localhost:3000/__cypress/runner/cypress_runner.js:6499:18)\n at Object.gotAccum (http://localhost:3000/__cypress/runner/cypress_runner.js:6488:25)\n at Object.tryCatcher (http://localhost:3000/__cypress/runner/cypress_runner.js:1830:23)\n at module.exports.Promise._settlePromiseFromHandler (http://localhost:3000/__cypress/runner/cypress_runner.js:1542:31)\n at module.exports.Promise._settlePromise (http://localhost:3000/__cypress/runner/cypress_runner.js:1599:18)\n at module.exports.Promise._settlePromiseCtx (http://localhost:3000/__cypress/runner/cypress_runner.js:1636:10)\n at _drainQueueStep (http://localhost:3000/__cypress/runner/cypress_runner.js:2434:12)\n at _drainQueue (http://localhost:3000/__cypress/runner/cypress_runner.js:2423:9)\n at Async._drainQueues (http://localhost:3000/__cypress/runner/cypress_runner.js:2439:5)\n at &lt;unknown&gt; (http://localhost:3000/__cypress/runner/cypress_runner.js:2309:14)\nFrom Your Spec Code:\n at Context.eval (webpack://dctomproject/./cypress/e2e/spec.cy.js:18:37)&quot;},&quot;uuid&quot;:&quot;7a7be8fb-3858-46cd-884c-349dd1de3ec4&quot;,&quot;parentUUID&quot;:&quot;b08d0fe5-99c2-4e84-8063-f358d2c8fc6e&quot;,&quot;isHook&quot;:false,&quot;skipped&quot;:false}],&quot;suites&quot;:[],&quot;passes&quot;:[&quot;57a4f280-853c-47a8-91c7-b3d5daf05f24&quot;],&quot;failures&quot;:[&quot;7a7be8fb-3858-46cd-884c-349dd1de3ec4&quot;],&quot;pending&quot;:[],&quot;skipped&quot;:[],&quot;duration&quot;:2756,&quot;root&quot;:false,&quot;rootEmpty&quot;:false,&quot;_timeout&quot;:2000}],&quot;passes&quot;:[],&quot;failures&quot;:[],&quot;pending&quot;:[],&quot;skipped&quot;:[],&quot;duration&quot;:0,&quot;root&quot;:true,&quot;rootEmpty&quot;:true,&quot;_timeout&quot;:2000}],&quot;meta&quot;:{&quot;mocha&quot;:{&quot;version&quot;:&quot;7.0.1&quot;},&quot;mochawesome&quot;:{&quot;options&quot;:{&quot;quiet&quot;:false,&quot;reportFilename&quot;:&quot;mochawesome&quot;,&quot;saveHtml&quot;:true,&quot;saveJson&quot;:true,&quot;consoleReporter&quot;:&quot;spec&quot;,&quot;useInlineDiffs&quot;:false,&quot;code&quot;:true},&quot;version&quot;:&quot;7.1.3&quot;},&quot;marge&quot;:{&quot;options&quot;:{&quot;reportDir&quot;:&quot;cypress/reports&quot;,&quot;overwrite&quot;:false,&quot;html&quot;:true,&quot;json&quot;:true,&quot;timestamp&quot;:&quot;mmddyyyy_HHMMss&quot;,&quot;reportTitle&quot;:&quot;DC-TOM Cypress Tests&quot;,&quot;reportPageTitle&quot;:&quot;DC-TOM 测试报告&quot;},&quot;version&quot;:&quot;6.2.0&quot;}}}" data-config="{&quot;reportFilename&quot;:&quot;mochawesome&quot;,&quot;reportDir&quot;:&quot;cypress/reports&quot;,&quot;reportTitle&quot;:&quot;DC-TOM Cypress Tests&quot;,&quot;reportPageTitle&quot;:&quot;DC-TOM 测试报告&quot;,&quot;inline&quot;:false,&quot;inlineAssets&quot;:false,&quot;cdn&quot;:false,&quot;charts&quot;:false,&quot;enableCharts&quot;:false,&quot;code&quot;:true,&quot;enableCode&quot;:true,&quot;autoOpen&quot;:false,&quot;overwrite&quot;:false,&quot;timestamp&quot;:&quot;mmddyyyy_HHMMss&quot;,&quot;ts&quot;:&quot;mmddyyyy_HHMMss&quot;,&quot;showPassed&quot;:true,&quot;showFailed&quot;:true,&quot;showPending&quot;:true,&quot;showSkipped&quot;:false,&quot;showHooks&quot;:&quot;failed&quot;,&quot;saveJson&quot;:true,&quot;saveHtml&quot;:true,&quot;dev&quot;:false,&quot;assetsDir&quot;:&quot;cypress/reports/assets&quot;,&quot;jsonFile&quot;:&quot;/Users/cw/Desktop/BME/dc-tom/cypress/reports/mochawesome_09022025_093321.json&quot;,&quot;htmlFile&quot;:&quot;/Users/cw/Desktop/BME/dc-tom/cypress/reports/mochawesome_09022025_093321.html&quot;}"><div id="report"></div><script src="assets/app.js"></script></body></html>
\ No newline at end of file
This diff is collapsed.
<!doctype html>
<html lang="en"><head><meta charSet="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>DC-TOM 测试报告</title><link rel="stylesheet" href="assets/app.css"/></head><body data-raw="{&quot;stats&quot;:{&quot;suites&quot;:1,&quot;tests&quot;:2,&quot;passes&quot;:1,&quot;pending&quot;:0,&quot;failures&quot;:1,&quot;start&quot;:&quot;2025-09-02T01:33:26.125Z&quot;,&quot;end&quot;:&quot;2025-09-02T01:33:48.994Z&quot;,&quot;duration&quot;:22869,&quot;testsRegistered&quot;:2,&quot;passPercent&quot;:50,&quot;pendingPercent&quot;:0,&quot;other&quot;:0,&quot;hasOther&quot;:false,&quot;skipped&quot;:0,&quot;hasSkipped&quot;:false},&quot;results&quot;:[{&quot;uuid&quot;:&quot;8bae2f11-f252-491a-837d-c74d1572556a&quot;,&quot;title&quot;:&quot;&quot;,&quot;fullFile&quot;:&quot;cypress/e2e/spec.cy.js&quot;,&quot;file&quot;:&quot;cypress/e2e/spec.cy.js&quot;,&quot;beforeHooks&quot;:[],&quot;afterHooks&quot;:[],&quot;tests&quot;:[],&quot;suites&quot;:[{&quot;uuid&quot;:&quot;b4095b36-e359-42cd-9f7c-50da145b3df8&quot;,&quot;title&quot;:&quot;template spec&quot;,&quot;fullFile&quot;:&quot;&quot;,&quot;file&quot;:&quot;&quot;,&quot;beforeHooks&quot;:[],&quot;afterHooks&quot;:[],&quot;tests&quot;:[{&quot;title&quot;:&quot;passes&quot;,&quot;fullTitle&quot;:&quot;template spec passes&quot;,&quot;duration&quot;:1651,&quot;state&quot;:&quot;passed&quot;,&quot;speed&quot;:&quot;fast&quot;,&quot;pass&quot;:true,&quot;fail&quot;:false,&quot;pending&quot;:false,&quot;code&quot;:&quot;cy.visit(&#x27;https://example.cypress.io&#x27;);&quot;,&quot;err&quot;:{},&quot;uuid&quot;:&quot;58ea6f27-d2fb-478e-86ab-a4900b8c3c13&quot;,&quot;parentUUID&quot;:&quot;b4095b36-e359-42cd-9f7c-50da145b3df8&quot;,&quot;isHook&quot;:false,&quot;skipped&quot;:false},{&quot;title&quot;:&quot;test1&quot;,&quot;fullTitle&quot;:&quot;template spec test1&quot;,&quot;duration&quot;:1209,&quot;state&quot;:&quot;failed&quot;,&quot;pass&quot;:false,&quot;fail&quot;:true,&quot;pending&quot;:false,&quot;code&quot;:&quot;/* ==== Generated with Cypress Studio ==== */\ncy.visit(&#x27;http://localhost:3000/&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-username-input\&quot;]&#x27;).clear(&#x27;z&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-username-input\&quot;]&#x27;).type(&#x27;zongheng_admin&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-password-input\&quot;]&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;login-password-input\&quot;]&#x27;).clear(&#x27;9%#F46vt&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-password-input\&quot;]&#x27;).type(&#x27;9%#F46vt&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-captcha-input\&quot;]&#x27;).clear(&#x27;8&#x27;);\ncy.get(&#x27;[data-testid=\&quot;login-captcha-input\&quot;]&#x27;).type(&#x27;8888&#x27;);\ncy.get(&#x27;.el-checkbox__label&#x27;).click();\ncy.get(&#x27;.el-checkbox__original&#x27;).check();\ncy.get(&#x27;[data-testid=\&quot;login-submit-button\&quot;]&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;menu-item-dust-overview\&quot;] &gt; span&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;dust-add-button\&quot;] &gt; span&#x27;).click();\ncy.get(&#x27;#el-id-7502-285&#x27;).click();\ncy.get(&#x27;#el-id-7502-97 &gt; span&#x27;).click();\ncy.get(&#x27;#el-id-7502-286&#x27;).clear(&#x27;1&#x27;);\ncy.get(&#x27;#el-id-7502-286&#x27;).type(&#x27;1&#x27;);\ncy.get(&#x27;#el-id-7502-287&#x27;).clear(&#x27;2&#x27;);\ncy.get(&#x27;#el-id-7502-287&#x27;).type(&#x27;2&#x27;);\ncy.get(&#x27;#el-id-7502-289&#x27;).click();\ncy.get(&#x27;.el-dialog__headerbtn &gt; .el-icon &gt; svg&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;dust-search-button\&quot;] &gt; span&#x27;).click();\ncy.get(&#x27;:nth-child(1) &gt; .el-table_1_column_10 &gt; .cell &gt; [data-testid=\&quot;dust-view-button\&quot;]&#x27;).click();\ncy.get(&#x27;[data-testid=\&quot;dust-monitoring-status-matrix\&quot;] &gt; .left &gt; :nth-child(1) &gt; :nth-child(1)&#x27;).click();\ncy.get(&#x27;.el-select__suffix &gt; .el-icon &gt; svg&#x27;).click();\ncy.get(&#x27;#el-id-7502-313 &gt; span&#x27;).click();\n/* ==== End Cypress Studio ==== */&quot;,&quot;err&quot;:{&quot;message&quot;:&quot;CypressError: `cy.check()` failed because this element is not visible:\n\n`&lt;input class=\&quot;el-checkbox__original\&quot; type=\&quot;checkbox\&quot; value=\&quot;记住密码\&quot;&gt;`\n\nThis element `&lt;input.el-checkbox__original&gt;` is not visible because it has an effective width and height of: `0 x 0` pixels.\n\nFix this problem, or use `{force: true}` to disable error checking.\n\nhttps://on.cypress.io/element-cannot-be-interacted-with&quot;,&quot;estack&quot;:&quot;CypressError: `cy.check()` failed because this element is not visible:\n\n`&lt;input class=\&quot;el-checkbox__original\&quot; type=\&quot;checkbox\&quot; value=\&quot;记住密码\&quot;&gt;`\n\nThis element `&lt;input.el-checkbox__original&gt;` is not visible because it has an effective width and height of: `0 x 0` pixels.\n\nFix this problem, or use `{force: true}` to disable error checking.\n\nhttps://on.cypress.io/element-cannot-be-interacted-with\n at runVisibilityCheck (http://localhost:3000/__cypress/runner/cypress_runner.js:144957:58)\n at Object.isVisible (http://localhost:3000/__cypress/runner/cypress_runner.js:144968:10)\n at checkOrUncheckEl (http://localhost:3000/__cypress/runner/cypress_runner.js:112342:24)\n at tryCatcher (http://localhost:3000/__cypress/runner/cypress_runner.js:1830:23)\n at Object.gotValue (http://localhost:3000/__cypress/runner/cypress_runner.js:6499:18)\n at Object.gotAccum (http://localhost:3000/__cypress/runner/cypress_runner.js:6488:25)\n at Object.tryCatcher (http://localhost:3000/__cypress/runner/cypress_runner.js:1830:23)\n at Promise._settlePromiseFromHandler (http://localhost:3000/__cypress/runner/cypress_runner.js:1542:31)\n at Promise._settlePromise (http://localhost:3000/__cypress/runner/cypress_runner.js:1599:18)\n at Promise._settlePromiseCtx (http://localhost:3000/__cypress/runner/cypress_runner.js:1636:10)\n at _drainQueueStep (http://localhost:3000/__cypress/runner/cypress_runner.js:2434:12)\n at _drainQueue (http://localhost:3000/__cypress/runner/cypress_runner.js:2423:9)\n at Async._drainQueues (http://localhost:3000/__cypress/runner/cypress_runner.js:2439:5)\n at &lt;unknown&gt; (http://localhost:3000/__cypress/runner/cypress_runner.js:2309:14)\nFrom Your Spec Code:\n at Context.eval (webpack://dctomproject/./cypress/e2e/spec.cy.js:18:37)&quot;},&quot;uuid&quot;:&quot;f7fd903c-575c-48a3-b5db-2f9d1fdd3901&quot;,&quot;parentUUID&quot;:&quot;b4095b36-e359-42cd-9f7c-50da145b3df8&quot;,&quot;isHook&quot;:false,&quot;skipped&quot;:false}],&quot;suites&quot;:[],&quot;passes&quot;:[&quot;58ea6f27-d2fb-478e-86ab-a4900b8c3c13&quot;],&quot;failures&quot;:[&quot;f7fd903c-575c-48a3-b5db-2f9d1fdd3901&quot;],&quot;pending&quot;:[],&quot;skipped&quot;:[],&quot;duration&quot;:2860,&quot;root&quot;:false,&quot;rootEmpty&quot;:false,&quot;_timeout&quot;:2000}],&quot;passes&quot;:[],&quot;failures&quot;:[],&quot;pending&quot;:[],&quot;skipped&quot;:[],&quot;duration&quot;:0,&quot;root&quot;:true,&quot;rootEmpty&quot;:true,&quot;_timeout&quot;:2000}],&quot;meta&quot;:{&quot;mocha&quot;:{&quot;version&quot;:&quot;7.0.1&quot;},&quot;mochawesome&quot;:{&quot;options&quot;:{&quot;quiet&quot;:false,&quot;reportFilename&quot;:&quot;mochawesome&quot;,&quot;saveHtml&quot;:true,&quot;saveJson&quot;:true,&quot;consoleReporter&quot;:&quot;spec&quot;,&quot;useInlineDiffs&quot;:false,&quot;code&quot;:true},&quot;version&quot;:&quot;7.1.3&quot;},&quot;marge&quot;:{&quot;options&quot;:{&quot;reportDir&quot;:&quot;cypress/reports&quot;,&quot;overwrite&quot;:false,&quot;html&quot;:true,&quot;json&quot;:true,&quot;timestamp&quot;:&quot;mmddyyyy_HHMMss&quot;,&quot;reportTitle&quot;:&quot;DC-TOM Cypress Tests&quot;,&quot;reportPageTitle&quot;:&quot;DC-TOM 测试报告&quot;},&quot;version&quot;:&quot;6.2.0&quot;}}}" data-config="{&quot;reportFilename&quot;:&quot;mochawesome&quot;,&quot;reportDir&quot;:&quot;cypress/reports&quot;,&quot;reportTitle&quot;:&quot;DC-TOM Cypress Tests&quot;,&quot;reportPageTitle&quot;:&quot;DC-TOM 测试报告&quot;,&quot;inline&quot;:false,&quot;inlineAssets&quot;:false,&quot;cdn&quot;:false,&quot;charts&quot;:false,&quot;enableCharts&quot;:false,&quot;code&quot;:true,&quot;enableCode&quot;:true,&quot;autoOpen&quot;:false,&quot;overwrite&quot;:false,&quot;timestamp&quot;:&quot;mmddyyyy_HHMMss&quot;,&quot;ts&quot;:&quot;mmddyyyy_HHMMss&quot;,&quot;showPassed&quot;:true,&quot;showFailed&quot;:true,&quot;showPending&quot;:true,&quot;showSkipped&quot;:false,&quot;showHooks&quot;:&quot;failed&quot;,&quot;saveJson&quot;:true,&quot;saveHtml&quot;:true,&quot;dev&quot;:false,&quot;assetsDir&quot;:&quot;cypress/reports/assets&quot;,&quot;jsonFile&quot;:&quot;/Users/cw/Desktop/BME/dc-tom/cypress/reports/mochawesome_09022025_093348.json&quot;,&quot;htmlFile&quot;:&quot;/Users/cw/Desktop/BME/dc-tom/cypress/reports/mochawesome_09022025_093348.html&quot;}"><div id="report"></div><script src="assets/app.js"></script></body></html>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment