UNPKG

@revoloo/cypress6

Version:

Cypress.io end to end testing tool

756 lines (599 loc) 26.9 kB
const onSubmitNewProject = function (orgId) { it('sends existing project name, org id, and visibility: private to ipc event by default', function () { cy.get('.setup-project') .contains('.btn', 'Set up project').click() .then(() => { expect(this.ipc.setupDashboardProject).to.be.calledWith({ projectName: this.config.projectName, orgId, public: false, }) }) }) it('sends modified project name, org id, and public flag to ipc event', function () { cy.get('#projectName').clear().type('New Project') cy.get('.privacy-selector').find('a').click() cy.get('.setup-project') .contains('.btn', 'Set up project').click() .then(() => { expect(this.ipc.setupDashboardProject).to.be.calledWith({ projectName: 'New Project', orgId, public: true, }) }) }) it('disables button and shows spinner', function () { cy.get('.setup-project') .contains('.btn', 'Set up project') .click() .should('be.disabled') .find('i') .should('be.visible') }) context('errors', function () { beforeEach(function () { cy.get('.setup-project') .contains('.btn', 'Set up project').click() }) it('logs user out when 401', function () { this.setupDashboardProject.reject({ name: '', message: '', statusCode: 401 }) cy.shouldBeLoggedOut() }) it('displays error name and message when unexpected', function () { this.setupDashboardProject.reject({ name: 'Fatal Error!', message: `{ "system": "down", "toxicity": "of the city" }`, }) cy.contains('"system": "down"') cy.percySnapshot() }) }) context('after submit', function () { beforeEach(function () { this.setupDashboardProject.resolve({ id: 'project-id-123', public: true, orgId, }) cy.get('.setup-project') .contains('.btn', 'Set up project').click() }) it('displays empty runs page', function () { cy.get('.setup-project').should('not.exist') cy.contains('To record your first') cy.contains('cypress run --record --key record-key-123') }) it('updates localStorage projects cache', function () { const org = Cypress._.find(this.orgs, { id: orgId }) expect(JSON.parse(localStorage.projects || '[]')[0].orgName).to.equal(org.name) }) }) } describe('Connect to Dashboard', function () { beforeEach(function () { cy.fixture('user').as('user') cy.fixture('projects').as('projects') cy.fixture('projects_statuses').as('projectStatuses') cy.fixture('config').as('config') cy.fixture('specs').as('specs') cy.fixture('organizations').as('orgs') cy.fixture('keys').as('keys') cy.fixture('dashboard_projects').as('dashboardProjects') cy.visitIndex().then(function (win) { let start = win.App.start this.win = win this.ipc = win.App.ipc this.config.projectName = 'my-kitchen-sink' cy.stub(this.ipc, 'getOptions').resolves({ projectRoot: '/foo/bar' }) cy.stub(this.ipc, 'updaterCheck').resolves(false) cy.stub(this.ipc, 'closeBrowser').resolves(null) this.config.projectId = null cy.stub(this.ipc, 'openProject').resolves(this.config) cy.stub(this.ipc, 'getSpecs').yields(null, this.specs) cy.stub(this.ipc, 'getRuns').resolves([]) cy.stub(this.ipc, 'getRecordKeys').resolves(this.keys) cy.stub(this.ipc, 'pingApiServer').resolves() cy.stub(this.ipc, 'externalOpen') cy.stub(this.ipc, 'setProjectId').resolvesArg(0) this.getCurrentUser = this.util.deferred() cy.stub(this.ipc, 'getCurrentUser').resolves(this.getCurrentUser.promise) this.getOrgs = this.util.deferred() cy.stub(this.ipc, 'getOrgs').returns(this.getOrgs.promise) this.getProjectStatus = this.util.deferred() cy.stub(this.ipc, 'getProjectStatus').returns(this.getProjectStatus.promise) this.getDashboardProjects = this.util.deferred() cy.stub(this.ipc, 'getDashboardProjects').returns(this.getDashboardProjects.promise) this.setupDashboardProject = this.util.deferred() cy.stub(this.ipc, 'setupDashboardProject').returns(this.setupDashboardProject.promise) start() cy.get('.navbar-default a') .contains('Runs').click() }) }) it('displays "need to set up" message', function () { cy.contains('Connect to the Dashboard to see your recorded test runs here') }) describe('when there is a current user', function () { beforeEach(function () { this.getCurrentUser.resolve(this.user) }) describe('general behavior', function () { beforeEach(function () { this.getOrgs.resolve(this.orgs) this.getDashboardProjects.resolve(this.dashboardProjects) cy.get('.btn').contains('Connect to Dashboard').click() }) it('clicking link opens setup project window', function () { cy.get('.setup-project').should('be.visible') cy.percySnapshot() }) it('closes when X is clicked', function () { cy.get('.close').click() cy.contains('Connect to the Dashboard to see your recorded test runs here') }) it('org docs are linked', function () { cy.contains('label', 'Who should own this') .find('a').click().then(function () { expect(this.ipc.externalOpen).to.be.calledWith('https://on.cypress.io/what-are-organizations') }) }) }) describe('loading behavior', function () { beforeEach(function () { cy.get('.btn').contains('Connect to Dashboard').click() }) it('calls getOrgs', function () { expect(this.ipc.getOrgs).to.be.calledOnce }) it('calls getDashboardProjects', function () { expect(this.ipc.getDashboardProjects).to.be.calledOnce }) it('displays loading view before both orgs and dashboard projects load', function () { cy.get('.loader').should('exist').then(function () { cy.percySnapshot() this.getOrgs.resolve(this.orgs) cy.get('.loader').should('exist').then(function () { this.getDashboardProjects.resolve(this.dashboardProjects) }) }) cy.get('.loader').should('not.exist') }) it('logs user out when getOrgs 401s', function () { this.getOrgs.reject({ name: '', message: '', statusCode: 401 }) cy.shouldBeLoggedOut() }) it('logs user out when getDashboardProjects 401s', function () { this.getDashboardProjects.reject({ name: '', message: '', statusCode: 401 }) cy.shouldBeLoggedOut() }) it('displays "set up" message in background on log out', function () { this.getDashboardProjects.reject({ name: '', message: '', statusCode: 401 }) cy.shouldBeLoggedOut() cy.contains('Connect to the Dashboard to see your recorded test runs here') }) }) describe('selecting an org', function () { context('with orgs', function () { beforeEach(function () { this.getOrgs.resolve(this.orgs) this.getDashboardProjects.resolve(this.dashboardProjects) cy.get('.btn').contains('Connect to Dashboard').click() cy.get('.setup-project') }) it('lists organizations to assign to project', function () { cy.get('.empty-select-orgs').should('not.exist') cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .should('have.length', this.orgs.length) cy.percySnapshot() }) it('selects personal org by default', function () { cy.get('.organizations-select').contains( 'Your personal organization', ) }) it('opens external link on click of manage', () => { cy.get('.manage-orgs-btn').click().then(function () { expect(this.ipc.externalOpen).to.be.calledWith('https://on.cypress.io/dashboard/organizations') }) }) }) context('orgs with no default org', function () { beforeEach(function () { this.getOrgs.resolve(Cypress._.filter(this.orgs, { 'default': false })) this.getDashboardProjects.resolve(Cypress._.filter(this.dashboardProjects, { 'orgDefault': false })) cy.get('.btn').contains('Connect to Dashboard').click() }) it('lists organizations to assign to project', function () { cy.get('.empty-select-orgs').should('not.exist') cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') // do not count the default org we removed .should('have.length', this.orgs.length - 1) }) it('selects first org by default', function () { cy.get('.organizations-select').contains(this.orgs[1].name) }) it('opens external link on click of manage', () => { cy.get('.manage-orgs-btn').click().then(function () { expect(this.ipc.externalOpen).to.be.calledWith('https://on.cypress.io/dashboard/organizations') }) }) }) context('without orgs', function () { beforeEach(function () { this.getOrgs.resolve([]) this.getDashboardProjects.resolve([]) cy.get('.btn').contains('Connect to Dashboard').click() }) it('displays empty message', () => { cy.get('.empty-select-orgs').should('be.visible') cy.get('.organizations-select').should('not.exist') cy.get('.project-select').should('not.exist') cy.get('#projectName').should('not.exist') cy.get('.privacy-selector').should('not.exist') cy.contains('.btn', 'Set up project').should('be.disabled') cy.percySnapshot() }) it('opens dashboard organizations when \'create org\' is clicked', () => { cy.contains('Create organization').click().then(function () { expect(this.ipc.externalOpen).to.be.calledWith('https://on.cypress.io/dashboard/organizations') }) }) }) context('with only default org', function () { beforeEach(function () { this.getOrgs.resolve([{ 'id': '000', 'name': 'Jane Lane', 'default': true, }]) this.getDashboardProjects.resolve(Cypress._.filter(this.dashboardProjects, { 'orgDefault': true })) cy.get('.btn').contains('Connect to Dashboard').click() cy.get('.setup-project') }) it('displays in dropdown', () => { cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option').should('have.length', 1) }) }) context('polls for updates to organizations', function () { beforeEach(function () { cy.clock() this.getOrgs.resolve(this.orgs) this.getDashboardProjects.resolve(this.dashboardProjects) cy.get('.btn').contains('Connect to Dashboard').click() }) it('polls for orgs twice in 10+sec on click of org', function () { cy.tick(11000).then(() => { expect(this.ipc.getOrgs).to.be.calledTwice }) }) it('updates org name on list on successful poll', function () { this.name = 'Foo Bar Devs' this.orgs[1].name = this.name this.getOrgsAgain = this.ipc.getOrgs.onCall(2).resolves(this.orgs) cy.tick(11000) cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains(this.name) }) it('adds new org to list on successful poll', function () { this.orgs.push({ 'id': '333', 'name': 'Ivory Developers', 'default': false, }) this.getOrgsAgain = this.ipc.getOrgs.onCall(2).resolves(this.orgs) cy.tick(11000) cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .should('have.length', this.orgs.length) }) }) }) describe('selecting or creating a project', function () { beforeEach(function () { this.getDashboardProjects.resolve(this.dashboardProjects) cy.get('.btn').contains('Connect to Dashboard').click() cy.get('.setup-project') }) context('default behavior', function () { it('displays new project when preselected org has no projects', function () { this.getOrgs.resolve(Cypress._.filter(this.orgs, { id: '999' })) cy.get('#projectName').should('exist') cy.get('.privacy-selector').should('exist') cy.get('.project-select').should('not.exist') }) it('displays project select when preselected org has existing projects', function () { this.getOrgs.resolve(Cypress._.filter(this.orgs, { id: '777' })) cy.get('.project-select').should('exist') cy.get('#projectName').should('not.exist') cy.get('.privacy-selector').should('not.exist') }) }) context('with org with existing projects', function () { beforeEach(function () { this.getOrgs.resolve(this.orgs) cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains('Acme Developers').click() }) it('displays dropdown of existing projects with id for that org only', function () { const orgProjects = Cypress._.filter(this.dashboardProjects, { 'orgName': 'Acme Developers' }) const otherProjects = Cypress._.reject(this.dashboardProjects, { 'orgName': 'Acme Developers' }) cy.contains('Select a project') cy.percySnapshot() cy.get('.project-select__dropdown-indicator').click() cy.get('.project-select__menu').should('be.visible') cy.wrap(orgProjects).each(function (project) { cy.get('.project-select__option').contains(`${project.name} (${project.id})`) }) cy.wrap(otherProjects).each(function (project) { cy.get('.project-select__option').contains(project.name).should('not.exist') }) }) it('sorts existing projects, displaying those without existing runs first', function () { cy.get('.project-select__dropdown-indicator').click() cy.get('.project-select__menu').should('be.visible') cy.get('.project-select__option').eq(0).contains(this.dashboardProjects[1].name) cy.get('.project-select__option').eq(1).contains(this.dashboardProjects[3].name) cy.get('.project-select__option').eq(2).contains(this.dashboardProjects[2].name) cy.percySnapshot() }) it('does not display name input or visibility selector', function () { cy.get('#projectName').should('not.exist') cy.get('.privacy-selector').should('not.exist') }) it('disables submit button until project is selected', function () { cy.get('.setup-project') .contains('.btn', 'Set up project') .should('be.disabled') cy.get('.project-select__dropdown-indicator').click() cy.get('.project-select__menu').should('be.visible') cy.get('.project-select__option') .contains(this.dashboardProjects[1].name).click() cy.get('.setup-project') .contains('.btn', 'Set up project') .should('not.be.disabled') }) it('switches to new project when org is changed to one without existing projects', function () { cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains('Osato Devs').click() cy.get('#projectName').should('exist') cy.get('.privacy-selector').should('exist') cy.get('.project-select').should('not.exist') }) context('on submit', function () { beforeEach(function () { cy.get('.project-select__dropdown-indicator').click() cy.get('.project-select__menu').should('be.visible') cy.get('.project-select__option').contains(this.dashboardProjects[1].name).click() }) it('calls ipc setProjectId event with selected id', function () { cy.get('.setup-project') .contains('.btn', 'Set up project').click() .then(() => { expect(this.ipc.setProjectId).to.be.calledWith(this.dashboardProjects[1].id) }) }) it('does not call ipc setupDashboardProject event', function () { cy.get('.setup-project') .contains('.btn', 'Set up project').click() .then(() => { expect(this.ipc.setupDashboardProject).not.to.be.called }) }) it('updates localStorage projects cache', function () { cy.get('.setup-project') .contains('.btn', 'Set up project').click() .then(() => { const localProjects = JSON.parse(localStorage.projects || '[]') expect(localProjects[0].id).to.equal(this.dashboardProjects[1].id) expect(localProjects[0].name).to.equal(this.dashboardProjects[1].name) }) }) }) context('creating a new project', function () { beforeEach(function () { cy.contains('Acme Developers') cy.get('.setup-project').contains('Create new project').click() }) it('displays name input and visibility selector with updated title', function () { cy.contains('What\'s the name of the project?') cy.get('#projectName').should('exist') cy.get('.privacy-selector').should('exist') cy.percySnapshot() }) it('does not display existing project selector', function () { cy.contains('Select a project').should('not.exist') cy.get('.project-select').should('not.exist') }) it('allows user to go back to existing projects', function () { cy.get('.setup-project').contains('Choose an existing project').click() cy.contains('Select a project') cy.get('.project-select__dropdown-indicator').should('exist') }) context('on submit', function () { onSubmitNewProject('777') }) }) }) context('with org without existing projects', function () { beforeEach(function () { this.getOrgs.resolve(this.orgs) cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains('Osato Devs').click() }) it('prefills Project Name', function () { cy.get('#projectName').should('have.value', this.config.projectName) cy.percySnapshot() }) it('allows me to change Project Name value', function () { cy.get('#projectName').clear().type('New Project Here') .should('have.value', 'New Project Here') }) it('submit button is enabled by default', function () { cy.get('.setup-project').contains('.btn', 'Set up project') .should('not.be.disabled') }) it('submit button is disabled when input is empty', function () { cy.get('#projectName').clear() cy.get('.setup-project').contains('.btn', 'Set up project') .should('be.disabled') }) it('does not display link to choose existing project', function () { cy.get('.setup-project').contains('Create new project').should('not.exist') }) it('switches to project dropdown when org is changed to one with existing projects', function () { cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains('Acme Developers').click() cy.get('#projectName').should('not.exist') cy.get('.project-select').should('exist') }) context('visibility', function () { it('is private by default', function () { cy.contains('Project visibility is set to Private.') }) it('can be toggled on click', function () { cy.contains('Project visibility is set to Private.') cy.get('.privacy-selector').find('a').click() cy.contains('Project visibility is set to Public.') cy.get('.privacy-selector').find('a').click() cy.contains('Project visibility is set to Private.') }) it('displays tooltip when set to private', function () { cy.get('.privacy-selector').children('span').trigger('mouseover') cy.get('.cy-tooltip').should('contain', 'Only invited users have access') }) it('displays tooltip when set to public', function () { cy.get('.privacy-selector').find('a').click() cy.get('.privacy-selector').children('span').trigger('mouseover') cy.get('.cy-tooltip').should('contain', 'Anyone has access') }) }) context('on submit', function () { onSubmitNewProject('999') }) }) }) describe('polls for updates to projects', function () { beforeEach(function () { cy.clock() this.getOrgs.resolve(this.orgs) this.getDashboardProjects.resolve(this.dashboardProjects) cy.get('.btn').contains('Connect to Dashboard').click() }) it('polls for dashboard projects twice in 10+sec', function () { cy.tick(11000).then(() => { expect(this.ipc.getDashboardProjects).to.be.calledTwice }) }) it('updates project name on list on successful poll', function () { const name = 'My Project Name' this.dashboardProjects[1].name = name this.ipc.getDashboardProjects.onCall(2).resolves(this.dashboardProjects) cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains('Acme Developers').click() cy.tick(11000) cy.get('.project-select__dropdown-indicator').click() cy.get('.project-select__menu').should('be.visible') cy.get('.project-select__option') .contains(name) }) it('adds new project to list on successful poll', function () { this.dashboardProjects.push({ ...this.dashboardProjects[0], 'orgId': '777', 'orgName': 'Acme Developers', 'orgDefault': false, }) this.ipc.getDashboardProjects.onCall(2).resolves(this.dashboardProjects) cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains('Acme Developers').click() cy.tick(11000) cy.get('.project-select__dropdown-indicator').click() cy.get('.project-select__menu').should('be.visible') cy.get('.project-select__option') .contains(this.dashboardProjects[0].name) }) it('stays on new project when additional project is added to org', function () { this.dashboardProjects.push({ ...this.dashboardProjects[0], 'orgId': '777', 'orgName': 'Acme Developers', 'orgDefault': false, }) this.ipc.getDashboardProjects.onCall(2).resolves(this.dashboardProjects) cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains('Acme Developers').click() cy.contains('Create new project').click() cy.get('#projectName').should('exist').clear().type('Project Name') cy.tick(11000) cy.get('#projectName').should('have.value', 'Project Name') cy.contains('Choose an existing project') }) it('stays on new project when first project is added to org', function () { this.dashboardProjects.push({ ...this.dashboardProjects[0], 'orgId': '999', 'orgName': 'Osato Devs', 'orgDefault': false, }) this.ipc.getDashboardProjects.onCall(2).resolves(this.dashboardProjects) cy.get('.organizations-select__dropdown-indicator').click() cy.get('.organizations-select__menu').should('be.visible') cy.get('.organizations-select__option') .contains('Osato Devs').click() cy.get('#projectName').should('exist').clear().type('Project Name') cy.contains('Choose an existing project').should('not.exist') cy.tick(11000) cy.get('#projectName').should('have.value', 'Project Name') cy.contains('Choose an existing project') }) }) }) describe('when there is no current user', function () { beforeEach(function () { this.getCurrentUser.resolve(null) cy.get('.btn').contains('Connect to Dashboard').click() }) it('shows login', () => { cy.get('.modal').contains('Log In to Dashboard') }) it('closes login modal', () => { cy.get('.modal').contains('Log In to Dashboard') cy.get('.close').click() cy.get('.btn').contains('Connect to Dashboard').click() }) describe('when login succeeds', function () { beforeEach(function () { cy.stub(this.ipc, 'beginAuth').resolves(this.user) cy.contains('button', 'Log In to Dashboard').click() }) it('shows setup', () => { cy.get('.login-content > .btn').click() cy.contains('h4', 'Set up project') }) }) }) })