test: login more directly (#44467)

* test: login more directly

* test: separate login from other visits

In a single test, Cypress can only visit within a single domain, hence
the separation.

* chore: fail slowly for all strategies

* test: user certified user for showing cert

* test: fix and cleanup certifications
This commit is contained in:
Oliver Eyton-Williams
2021-12-11 10:04:16 +01:00
committed by GitHub
parent 8fb945c5a8
commit 48f88428e8
11 changed files with 100 additions and 157 deletions

View File

@ -62,6 +62,7 @@ jobs:
runs-on: ubuntu-20.04
needs: build-client
strategy:
fail-fast: false
matrix:
browsers: [chrome, firefox, electron]
node-version: [16.x]

View File

@ -15,6 +15,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node-version: [16.x]
@ -47,6 +48,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node-version: [16.x]

View File

@ -10,6 +10,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node-version: [16.x]
@ -39,6 +40,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node-version: [16.x]
@ -70,6 +72,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node-version: [16.x]
@ -103,6 +106,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
node-version: [16.x]
locale: [chinese, espanol]

View File

@ -1,112 +1,44 @@
import { SuperBlocks } from '../../config/certification-settings';
const certificationUrl = '/certification/developmentuser/responsive-web-design';
const projects = {
superBlock: SuperBlocks.RespWebDesign,
block: 'responsive-web-design-projects',
challenges: [
{
slug: 'build-a-tribute-page',
solution: 'https://codepen.io/moT01/pen/ZpJpKp'
},
{
slug: 'build-a-survey-form',
solution: 'https://codepen.io/moT01/pen/LrrjGz?editors=1010'
},
{
slug: 'build-a-product-landing-page',
solution: 'https://codepen.io/moT01/full/qKyKYL/'
},
{
slug: 'build-a-technical-documentation-page',
solution: 'https://codepen.io/moT01/full/JBvzNL/'
},
{
slug: 'build-a-personal-portfolio-webpage',
solution: 'https://codepen.io/moT01/pen/vgOaoJ'
}
]
};
const certifiedUser = '/certification/certifieduser/responsive-web-design';
describe('A certification,', function () {
before(() => {
cy.exec('npm run seed');
cy.login();
// submit projects for certificate
const { superBlock, block, challenges } = projects;
challenges.forEach(({ slug, solution }) => {
const url = `/learn/${superBlock}/${block}/${slug}`;
cy.visit(url);
cy.get('#dynamic-front-end-form')
.get('#solution')
.type(solution, { force: true, delay: 0 });
cy.contains("I've completed this challenge")
.should('not.be.disabled')
.click();
cy.contains('Submit and go to next challenge').click().wait(1000);
});
cy.get('.donation-modal').should('be.visible');
cy.visit('/settings');
// set user settings to public to claim a cert
cy.get('label:contains(Public)>input').each(el => {
if (!/toggle-active/.test(el[0].parentElement.className)) {
cy.wrap(el).click({ force: true });
cy.wait(1000);
}
});
// if honest policy not accepted
cy.get('.honesty-policy button').then(btn => {
if (btn[0].innerText === 'Agree') {
btn[0].click({ force: true });
cy.wait(1000);
}
});
// claim certificate
cy.get('a[href*="developmentuser/responsive-web-design"]').click({
force: true
});
cy.exec('npm run seed:certified-user');
});
describe('while viewing your own,', function () {
before(() => {
beforeEach(() => {
cy.login();
cy.visit(certificationUrl);
});
it('should render a LinkedIn button', function () {
cy.visit(certifiedUser);
cy.contains('Add this certification to my LinkedIn profile')
.should('have.attr', 'href')
.and(
'match',
// eslint-disable-next-line max-len
/https:\/\/www\.linkedin\.com\/profile\/add\?startTask=CERTIFICATION_NAME&name=Responsive Web Design&organizationId=4831032&issueYear=\d\d\d\d&issueMonth=\d\d?&certUrl=https:\/\/freecodecamp\.org\/certification\/developmentuser\/responsive-web-design/
/https:\/\/www\.linkedin\.com\/profile\/add\?startTask=CERTIFICATION_NAME&name=Responsive Web Design&organizationId=4831032&issueYear=\d\d\d\d&issueMonth=\d\d?&certUrl=https:\/\/freecodecamp\.org\/certification\/certifieduser\/responsive-web-design/
);
});
it('should render a Twitter button', function () {
cy.visit(certifiedUser);
cy.contains('Share this certification on Twitter').should(
'have.attr',
'href',
'https://twitter.com/intent/tweet?text=I just earned the Responsive Web Design certification @freeCodeCamp! Check it out here: https://freecodecamp.org/certification/developmentuser/responsive-web-design'
'https://twitter.com/intent/tweet?text=I just earned the Responsive Web Design certification @freeCodeCamp! Check it out here: https://freecodecamp.org/certification/certifieduser/responsive-web-design'
);
});
it("should be issued with today's date", () => {
const date = new Date();
const issued = `Issued\xa0${new Intl.DateTimeFormat('en-US', {
month: 'long'
}).format(date)} ${date.getDate()}, ${date.getFullYear()}`;
it('should be issued with the submission date', () => {
cy.visit(certifiedUser);
const issued = `Issued\xa0August 3, 2018`;
cy.get('[data-cy=issue-date]').should('have.text', issued);
});
});
describe("while viewing someone else's,", function () {
before(() => {
cy.visit(certificationUrl);
cy.visit(certifiedUser);
});
it('should display certificate', function () {

View File

@ -34,6 +34,7 @@ const pythonProjects = {
describe('project submission', () => {
beforeEach(() => {
cy.exec('npm run seed');
cy.login();
});
// NOTE: this will fail once challenge tests are added.
it('Should be possible to submit Python projects', () => {
@ -46,7 +47,7 @@ describe('project submission', () => {
.type('https://replit.com/@camperbot/python-project#main.py');
cy.contains("I've completed this challenge").click();
cy.contains('Go to next challenge');
cy.contains('go to next challenge');
// clicking on 'Go to next challenge' seems to have caused flakiness, so
// it's commented out until we figure out why.
// cy.contains('Go to next challenge').click();
@ -61,7 +62,6 @@ describe('project submission', () => {
'JavaScript projects can be submitted and then viewed in /settings and on the certifications',
{ browser: 'electron' },
() => {
cy.login();
cy.fixture('../../config/curriculum.json').then(curriculum => {
const { challenges, meta } =
curriculum[SuperBlocks.JsAlgoDataStruct].blocks[

View File

@ -32,7 +32,7 @@ function waitForAppStart() {
});
}
describe('Navbar', () => {
describe('Navbar when logged out', () => {
beforeEach(() => {
appHasStarted = false;
cy.visit('/', {
@ -41,6 +41,32 @@ describe('Navbar', () => {
cy.viewport(1300, 660);
});
it('Should have a "Sign in" button', () => {
cy.contains("[data-test-label='landing-small-cta']", 'Sign in');
});
it(
'Should have `Sign in` link on landing and learn pages' +
' when not signed in',
() => {
cy.contains(selectors.smallCallToAction, 'Sign in');
cy.get(selectors.menuButton).click();
cy.get(selectors.navigationLinks).contains('Curriculum').click();
cy.contains(selectors.smallCallToAction, 'Sign in');
}
);
});
describe('Navbar Logged in', () => {
beforeEach(() => {
cy.login();
appHasStarted = false;
cy.visit('/', {
onBeforeLoad: spyOnListener
}).then(waitForAppStart);
cy.viewport(1300, 660);
});
it('Should render properly', () => {
cy.get('#universal-nav').should('be.visible');
cy.get('#universal-nav').should('have.class', 'universal-nav');
@ -56,10 +82,6 @@ describe('Navbar', () => {
}
);
it('Should have a "Sign in" button', () => {
cy.contains("[data-test-label='landing-small-cta']", 'Sign in');
});
// have the curriculum and CTA on landing and /learn pages.
it(
'Should have `Radio`, `Forum`, and `Curriculum` links on landing and learn pages' +
@ -75,32 +97,18 @@ describe('Navbar', () => {
}
);
it(
'Should have `Sign in` link on landing and learn pages' +
' when not signed in',
() => {
cy.contains(selectors.smallCallToAction, 'Sign in');
cy.get(selectors.menuButton).click();
cy.get(selectors.navigationLinks).contains('Curriculum').click();
cy.contains(selectors.smallCallToAction, 'Sign in');
}
);
it('Should have `Profile` link when user is signed in', () => {
cy.login();
cy.get(selectors.menuButton).click();
cy.get(selectors.navigationLinks).contains('Profile').click();
cy.url().should('include', '/developmentuser');
});
it('Should have a profile image with class `default-border`', () => {
cy.login();
cy.get(selectors.avatarContainer).should('have.class', 'default-border');
cy.get(selectors.defaultAvatar).should('exist');
});
it('Should have a profile image with dimensions that are <= 31px', () => {
cy.login();
cy.get(selectors.avatarImage).invoke('width').should('lte', 31);
cy.get(selectors.avatarImage).invoke('height').should('lte', 31);
});

View File

@ -1,57 +1,40 @@
import '@testing-library/cypress/add-commands';
describe('Settings certifications area', () => {
before(() => {
cy.exec('npm run seed');
cy.login();
cy.visit('/settings');
});
describe('initially', () => {
it('Should render 15 "Claim Certification" buttons', () => {
before(() => {
cy.exec('npm run seed');
cy.login();
});
it('Should render the default settings page', () => {
cy.visit('/settings/');
cy.findAllByText('Claim Certification').should($btns => {
expect($btns).to.have.length(15);
});
cy.findByText('Show Certification').should('not.exist');
cy.contains('Agree');
cy.contains('Claim Certification').click();
cy.contains(
'To claim a certification, you must first accept our academic honesty policy'
);
});
});
describe('after isHonest', () => {
before(() => {
cy.exec('npm run seed');
cy.login();
});
it('Should render zero "Show Certification" buttons', () => {
cy.contains('Show Certification').should('not.exist');
});
it('Should render one "Agree" button', () => {
cy.contains('Agree').should('exist');
});
describe('before isHonest', () => {
it('Should show "must agree" message when trying to claim a cert', () => {
cy.contains('Claim Certification').click();
cy.contains(
'To claim a certification, you must first accept our academic honesty policy'
).should('exist');
});
});
describe('after isHonest', () => {
beforeEach(() => {
cy.visit('/');
cy.login();
cy.visit('/settings');
});
it('Should render "You have accepted our Academic Honesty Policy." button after clicking "Agree"', () => {
cy.contains('Agree').click({ force: true });
cy.contains('You have accepted our Academic Honesty Policy.').should(
'exist'
);
});
it('Should show "incompleted projects" message when clicking "Claim Certification"', () => {
cy.contains('Claim Certification').click({ force: true });
cy.contains(
'It looks like you have not completed the necessary steps. Please complete the required projects to claim the Responsive Web Design Certification'
).should('exist');
});
it('Should update the user as they try to claim their certifications', () => {
cy.visit('/settings');
cy.contains('Agree').click();
cy.contains('You have accepted our Academic Honesty Policy.');
cy.contains('Claim Certification').click();
cy.contains(
'It looks like you have not completed the necessary steps. Please complete the required projects to claim the Responsive Web Design Certification'
);
});
});
});

View File

@ -1,6 +1,8 @@
describe('Settings', () => {
it('should be possible to reset your progress', () => {
before(() => {
cy.login();
});
it('should be possible to reset your progress', () => {
cy.visit('/settings');
cy.contains('Reset all of my progress').click();
cy.contains('Reset everything. I want to start from the beginning').click();

View File

@ -1,14 +1,18 @@
describe('Username input field', () => {
beforeEach(() => {
cy.login();
});
function goToSettings() {
cy.visit('/settings');
// Setting aliases here
cy.get('input[name=username-settings]').as('usernameInput');
cy.get('form#usernameSettings').as('usernameForm');
});
}
it('Should be possible to type', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('twaha', { force: true })
@ -16,6 +20,7 @@ describe('Username input field', () => {
});
it('Should show message when validating name', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('twaha', { force: true });
@ -28,6 +33,7 @@ describe('Username input field', () => {
});
it('Should show username is available if it is', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('brad', { force: true });
@ -41,6 +47,7 @@ describe('Username input field', () => {
});
it('Should info message if username is available', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('mrugesh', { force: true });
@ -58,6 +65,7 @@ describe('Username input field', () => {
// eslint-disable-next-line
it('Should be able to click the `Save` button if username is avalable', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('oliver', { force: true });
@ -68,6 +76,7 @@ describe('Username input field', () => {
});
it('Should show username is unavailable if it is', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('twaha', { force: true });
@ -82,6 +91,7 @@ describe('Username input field', () => {
// eslint-disable-next-line
it('Should not be possible to click the `Save` button if username is unavailable', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('twaha', { force: true });
@ -97,6 +107,7 @@ describe('Username input field', () => {
});
it('Should not show anything if user types their current name', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('developmentuser', { force: true });
@ -106,6 +117,7 @@ describe('Username input field', () => {
// eslint-disable-next-line max-len
it('Should not be possible to click the `Save` button if user types their current name', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('developmentuser', { force: true });
@ -114,6 +126,7 @@ describe('Username input field', () => {
});
it('Should show warning if username includes invalid character', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('Quincy Larson', { force: true });
@ -128,6 +141,7 @@ describe('Username input field', () => {
// eslint-disable-next-line max-len
it('Should not be able to click the `Save` button if username includes invalid character', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('Quincy Larson', { force: true });
@ -136,6 +150,7 @@ describe('Username input field', () => {
});
it('Should change username if `Save` button is clicked', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('quincy', { force: true });
@ -149,6 +164,7 @@ describe('Username input field', () => {
});
it('Should change username with uppercase characters if `Save` button is clicked', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('Quincy', { force: true });
@ -162,6 +178,7 @@ describe('Username input field', () => {
});
it('Should show flash message showing username has been updated', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('nhcarrigan', { force: true });
@ -181,6 +198,7 @@ describe('Username input field', () => {
});
it('Should be able to close the shown flash message', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('bjorno', { force: true });
@ -197,6 +215,7 @@ describe('Username input field', () => {
});
it('Should change username if enter is pressed', () => {
goToSettings();
cy.get('@usernameInput')
.clear({ force: true })
.type('symbol', { force: true });

View File

@ -33,14 +33,7 @@
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => {});
Cypress.Commands.add('login', () => {
cy.visit('/');
cy.contains("Get started (it's free)").click();
cy.location({ timeout: 10000 }).should(loc => {
// I'm not 100% sure why logins get redirected to /learn/ via 301 in
// development, but not in production, but they do. Hence to make it easier
// work on tests, we'll just allow for both.
expect(loc.pathname).to.match(/^\/learn\/?$/);
});
cy.visit('http://localhost:3000/signin');
cy.contains('Welcome back');
});
@ -60,7 +53,6 @@ Cypress.Commands.add('toggleAll', () => {
});
Cypress.Commands.add('resetUsername', () => {
cy.login();
cy.visit('/settings');
cy.get('@usernameInput')

View File

@ -4635,7 +4635,7 @@ module.exports = {
rand: 0.6126749173148205,
theme: 'default',
profileUI: {
isLocked: true,
isLocked: false,
showAbout: true,
showCerts: true,
showDonation: true,