feat(test, e2e) test suit for cypress (#42138)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
128
cypress/integration/main-tests/ShowCertification.js
Normal file
128
cypress/integration/main-tests/ShowCertification.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/* global cy */
|
||||
const certificationUrl = '/certification/developmentuser/responsive-web-design';
|
||||
const projects = {
|
||||
superBlock: 'responsive-web-design',
|
||||
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'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
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.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
|
||||
});
|
||||
});
|
||||
|
||||
describe('while viewing your own,', function () {
|
||||
before(() => {
|
||||
cy.login();
|
||||
cy.visit(certificationUrl);
|
||||
});
|
||||
|
||||
it('should render a LinkedIn button', function () {
|
||||
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/
|
||||
);
|
||||
});
|
||||
|
||||
it('should render a Twitter button', function () {
|
||||
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'
|
||||
);
|
||||
});
|
||||
|
||||
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()}`;
|
||||
cy.get('[data-cy=issue-date]').should('have.text', issued);
|
||||
});
|
||||
});
|
||||
|
||||
describe("while viewing someone else's,", function () {
|
||||
before(() => {
|
||||
cy.visit(certificationUrl);
|
||||
});
|
||||
|
||||
it('should display certificate', function () {
|
||||
cy.contains('has successfully completed the freeCodeCamp.org').should(
|
||||
'exist'
|
||||
);
|
||||
cy.contains('Responsive Web Design').should('exist');
|
||||
});
|
||||
|
||||
it('should not render a LinkedIn button', function () {
|
||||
cy.contains('Add this certification to my LinkedIn profile').should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render a Twitter button', function () {
|
||||
cy.contains('Share this certification on Twitter').should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
70
cypress/integration/main-tests/landing.js
Normal file
70
cypress/integration/main-tests/landing.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/* global cy */
|
||||
const selectors = {
|
||||
heading: "[data-test-label='landing-header']",
|
||||
callToAction: "[data-test-label='landing-big-cta']",
|
||||
certifications: "[data-test-label='certifications']",
|
||||
testimonials: "[data-test-label='testimonial-cards']",
|
||||
landingPageImage: '.landing-page-image'
|
||||
};
|
||||
|
||||
const certifications = [
|
||||
'Responsive Web Design',
|
||||
'JavaScript Algorithms and Data Structures',
|
||||
'Front End Development Libraries',
|
||||
'Data Visualization',
|
||||
'APIs and Microservices',
|
||||
'Quality Assurance',
|
||||
'Scientific Computing with Python',
|
||||
'Data Analysis with Python',
|
||||
'Information Security',
|
||||
'Machine Learning with Python'
|
||||
];
|
||||
|
||||
describe('Landing page', () => {
|
||||
it('Should render', () => {
|
||||
cy.visit('/');
|
||||
cy.title().should(
|
||||
'eq',
|
||||
'Learn to Code — For Free — Coding Courses for Busy People'
|
||||
);
|
||||
cy.contains(selectors.callToAction, "Get started (it's free)");
|
||||
cy.get(selectors.callToAction).should('have.length', 2);
|
||||
});
|
||||
|
||||
it('Has visible header and sub-header', () => {
|
||||
cy.contains(selectors.heading, 'Learn to code — for free.');
|
||||
cy.contains('Build projects.').should('be.visible');
|
||||
cy.contains('Earn certifications.').should('be.visible');
|
||||
|
||||
cy.contains(
|
||||
'Since 2014, more than 40,000 freeCodeCamp.org ' +
|
||||
'graduates have gotten jobs at tech companies including:'
|
||||
).should('be.visible');
|
||||
});
|
||||
|
||||
it('Has 5 brand logos', () => {
|
||||
cy.get('.logo-row').children().its('length').should('eq', 5);
|
||||
});
|
||||
|
||||
it('Has `as seens as` section', () => {
|
||||
cy.contains('Build projects.').should('be.visible');
|
||||
cy.get('.big-heading').siblings().get('svg');
|
||||
});
|
||||
|
||||
it('Has a visible large image on large viewports', function () {
|
||||
cy.viewport(1200, 660).get(selectors.landingPageImage).should('be.visible');
|
||||
|
||||
cy.viewport(1199, 660).get(selectors.landingPageImage).should('not.exist');
|
||||
});
|
||||
|
||||
it('Has links to all the certifications', function () {
|
||||
cy.get(selectors.certifications).children().its('length').should('eq', 10);
|
||||
cy.wrap(certifications).each(cert => {
|
||||
cy.get(selectors.certifications).contains(cert);
|
||||
});
|
||||
});
|
||||
|
||||
it('Has 3 testimonial cards', function () {
|
||||
cy.get(selectors.testimonials).children().its('length').should('eq', 3);
|
||||
});
|
||||
});
|
41
cypress/integration/main-tests/learn/challenges/backend.js
Normal file
41
cypress/integration/main-tests/learn/challenges/backend.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/* global cy */
|
||||
|
||||
const locations = {
|
||||
index:
|
||||
'learn/apis-and-microservices/managing-packages-with-npm/' +
|
||||
'how-to-use-package-json-the-core-of-any-node-js-project-or-npm-package'
|
||||
};
|
||||
|
||||
const selectors = {
|
||||
defaultOutput: '.output-text',
|
||||
input: 'input[name="solution"]'
|
||||
};
|
||||
|
||||
const unhandledErrorMessage = 'Something is not quite right';
|
||||
const runningOutput = '// running tests';
|
||||
const finishedOutput = '// tests completed';
|
||||
|
||||
describe('Backend challenge', function () {
|
||||
it('renders', () => {
|
||||
cy.visit(locations.index);
|
||||
|
||||
cy.title().should(
|
||||
'eq',
|
||||
'Managing Packages with Npm - How to Use package.json, the Core of Any' +
|
||||
' Node.js Project or npm Package | Learn | freeCodeCamp.org'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not generate unhandled errors on submission', () => {
|
||||
cy.visit(locations.index);
|
||||
cy.get(selectors.input)
|
||||
.type('https://example.com')
|
||||
.type('{enter}')
|
||||
.then(() => {
|
||||
cy.get(selectors.defaultOutput)
|
||||
.contains(runningOutput)
|
||||
.contains(finishedOutput);
|
||||
cy.contains(unhandledErrorMessage).should('not.exist');
|
||||
});
|
||||
});
|
||||
});
|
64
cypress/integration/main-tests/learn/challenges/output.js
Normal file
64
cypress/integration/main-tests/learn/challenges/output.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/* global cy */
|
||||
|
||||
const selectors = {
|
||||
defaultOutput: '.output-text',
|
||||
editor: '.monaco-editor',
|
||||
hotkeys: '.default-layout > div',
|
||||
runTestsButton: 'button:contains("Run the Tests")'
|
||||
};
|
||||
|
||||
const locations = {
|
||||
index:
|
||||
'/learn/responsive-web-design/basic-html-and-html5/' +
|
||||
'say-hello-to-html-elements'
|
||||
};
|
||||
|
||||
const defaultOutput = `
|
||||
/**
|
||||
* Your test output will go here
|
||||
*/`;
|
||||
|
||||
const runningOutput = '// running tests';
|
||||
const finishedOutput = '// tests completed';
|
||||
|
||||
describe('Classic challenge', function () {
|
||||
before(() => {
|
||||
cy.visit(locations.index);
|
||||
});
|
||||
|
||||
it('renders the default output text', () => {
|
||||
cy.title().should(
|
||||
'eq',
|
||||
'Learn Basic HTML and HTML5: Say Hello to HTML Elements |' +
|
||||
' freeCodeCamp.org'
|
||||
);
|
||||
cy.get(selectors.defaultOutput).contains(defaultOutput);
|
||||
});
|
||||
|
||||
it('shows test output when the tests are run', () => {
|
||||
// first wait for the editor to load
|
||||
cy.get(selectors.editor, { timeout: 15000 });
|
||||
cy.get(selectors.runTestsButton)
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.get(selectors.defaultOutput)
|
||||
.contains(runningOutput)
|
||||
.contains(finishedOutput);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows test output when the tests are triggered by the keyboard', () => {
|
||||
// first wait for the editor to load
|
||||
cy.get(selectors.editor, {
|
||||
timeout: 15000
|
||||
});
|
||||
cy.get(selectors.hotkeys)
|
||||
.focus()
|
||||
.type('{ctrl}{enter}')
|
||||
.then(() => {
|
||||
cy.get(selectors.defaultOutput)
|
||||
.contains(runningOutput)
|
||||
.contains(finishedOutput);
|
||||
});
|
||||
});
|
||||
});
|
52
cypress/integration/main-tests/learn/challenges/projects.js
Normal file
52
cypress/integration/main-tests/learn/challenges/projects.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/* global cy */
|
||||
|
||||
const projects = {
|
||||
superBlock: 'machine-learning-with-python',
|
||||
block: 'machine-learning-with-python-projects',
|
||||
challenges: [
|
||||
{
|
||||
slug: 'book-recommendation-engine-using-knn',
|
||||
nextChallengeText: 'Linear Regression Health Costs Calculator'
|
||||
},
|
||||
{
|
||||
slug: 'cat-and-dog-image-classifier',
|
||||
nextChallengeText: 'Book Recommendation Engine using KNN'
|
||||
},
|
||||
{
|
||||
slug: 'linear-regression-health-costs-calculator',
|
||||
nextChallengeText: 'Neural Network SMS Text Classifier'
|
||||
},
|
||||
{
|
||||
slug: 'neural-network-sms-text-classifier',
|
||||
nextChallengeText: 'Find the Symmetric Difference'
|
||||
},
|
||||
{
|
||||
slug: 'rock-paper-scissors',
|
||||
nextChallengeText: 'Cat and Dog Image Classifier'
|
||||
}
|
||||
]
|
||||
};
|
||||
describe('project submission', () => {
|
||||
// NOTE: this will fail once challenge tests are added.
|
||||
it('Should be possible to submit Python projects', () => {
|
||||
const { superBlock, block, challenges } = projects;
|
||||
challenges.forEach(({ slug }) => {
|
||||
const url = `/learn/${superBlock}/${block}/${slug}`;
|
||||
cy.visit(url);
|
||||
cy.get('#dynamic-front-end-form')
|
||||
.get('#solution')
|
||||
.type('https://replit.com/@camperbot/python-project#main.py');
|
||||
|
||||
cy.contains("I've completed this challenge").click();
|
||||
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();
|
||||
|
||||
// The next two commands are to confirm that go to next challenge has
|
||||
// moved us to the expected challenge before we loop again.
|
||||
// cy.get('.title-text').should('include.text', nextChallengeText);
|
||||
// cy.url().should('not.have.string', url);
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,19 @@
|
||||
/* global cy */
|
||||
|
||||
describe('Certification intro page', () => {
|
||||
before(() => {
|
||||
cy.clearCookies();
|
||||
cy.login();
|
||||
cy.visit('/learn/coding-interview-prep');
|
||||
});
|
||||
|
||||
it('Should render', () => {
|
||||
cy.contains(
|
||||
"If you're looking for free coding exercises to prepare for your next job interview, we've got you covered."
|
||||
).should('be.visible');
|
||||
});
|
||||
|
||||
it('Title should not include the word "Certification"', () => {
|
||||
cy.title().should('eq', 'Coding Interview Prep | freeCodeCamp.org');
|
||||
});
|
||||
});
|
@@ -0,0 +1,31 @@
|
||||
/* global cy */
|
||||
|
||||
const selectors = {
|
||||
footer: '.site-footer'
|
||||
};
|
||||
|
||||
describe('Footer', () => {
|
||||
it('Should render on landing page', () => {
|
||||
cy.visit('/');
|
||||
cy.get(selectors.footer).should('be.visible');
|
||||
});
|
||||
|
||||
it('Should render on learn page', () => {
|
||||
cy.visit('/learn');
|
||||
cy.get(selectors.footer).should('be.visible');
|
||||
cy.visit('/learn/');
|
||||
cy.get(selectors.footer).should('be.visible');
|
||||
});
|
||||
|
||||
it('Should render on superblock page', () => {
|
||||
cy.visit('/learn/responsive-web-design/');
|
||||
cy.get(selectors.footer).should('be.visible');
|
||||
});
|
||||
|
||||
it('Should not render on challenge page', () => {
|
||||
cy.visit(
|
||||
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements'
|
||||
);
|
||||
cy.get(selectors.footer).should('not.exist');
|
||||
});
|
||||
});
|
@@ -0,0 +1,36 @@
|
||||
/* global cy */
|
||||
|
||||
describe('Help Button', () => {
|
||||
it('should be visible', () => {
|
||||
cy.visit(
|
||||
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements'
|
||||
);
|
||||
cy.get('#get-help-dropdown').scrollIntoView().should('be.visible');
|
||||
});
|
||||
|
||||
it('should toggle the dropdown menu', () => {
|
||||
cy.get('#get-help-dropdown').scrollIntoView().click();
|
||||
cy.get('ul[role="menu"]').should('be.visible');
|
||||
});
|
||||
|
||||
it('should render three links when video is available', () => {
|
||||
cy.get('ul[role="menu"]').within(() => {
|
||||
cy.get('a').should('have.length', 3);
|
||||
cy.get('a').eq(0).contains('Get a Hint');
|
||||
cy.get('a').eq(1).contains('Watch a Video');
|
||||
cy.get('a').eq(2).contains('Ask for Help');
|
||||
});
|
||||
});
|
||||
|
||||
it('should render two links when video is not available', () => {
|
||||
cy.visit(
|
||||
'/learn/front-end-libraries/bootstrap/apply-the-default-bootstrap-button-style'
|
||||
);
|
||||
cy.get('#get-help-dropdown').scrollIntoView().click();
|
||||
cy.get('ul[role="menu"]').within(() => {
|
||||
cy.get('a').should('have.length', 2);
|
||||
cy.get('a').eq(0).contains('Get a Hint');
|
||||
cy.get('a').eq(1).contains('Ask for Help');
|
||||
});
|
||||
});
|
||||
});
|
109
cypress/integration/main-tests/learn/common-components/navbar.js
Normal file
109
cypress/integration/main-tests/learn/common-components/navbar.js
Normal file
@@ -0,0 +1,109 @@
|
||||
/* global cy */
|
||||
|
||||
const selectors = {
|
||||
heading: "[data-test-label='landing-header']",
|
||||
smallCallToAction: "[data-test-label='landing-small-cta']",
|
||||
navigationLinks: '.nav-list',
|
||||
avatarContainer: '.avatar-container',
|
||||
defaultAvatar: '.avatar-container',
|
||||
menuButton: '.toggle-button-nav',
|
||||
avatarImage: '.avatar-container .avatar'
|
||||
};
|
||||
|
||||
let appHasStarted;
|
||||
function spyOnListener(win) {
|
||||
const addListener = win.EventTarget.prototype.addEventListener;
|
||||
win.EventTarget.prototype.addEventListener = function (name) {
|
||||
if (name === 'click') {
|
||||
appHasStarted = true;
|
||||
win.EventTarget.prototype.addEventListener = addListener;
|
||||
}
|
||||
return addListener.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
function waitForAppStart() {
|
||||
return new Promise(resolve => {
|
||||
const isReady = () => {
|
||||
if (appHasStarted) {
|
||||
return resolve();
|
||||
}
|
||||
return setTimeout(isReady, 0);
|
||||
};
|
||||
isReady();
|
||||
});
|
||||
}
|
||||
|
||||
describe('Navbar', () => {
|
||||
beforeEach(() => {
|
||||
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');
|
||||
});
|
||||
|
||||
it(
|
||||
'Should take user to learn page when clicked on ' + 'the freeCodeCamp logo',
|
||||
() => {
|
||||
cy.get('.universal-nav-middle').within(() => {
|
||||
cy.get('svg').click();
|
||||
});
|
||||
cy.url().should('include', '/learn');
|
||||
}
|
||||
);
|
||||
|
||||
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' +
|
||||
'page when not signed in',
|
||||
() => {
|
||||
cy.get(selectors.menuButton).click();
|
||||
cy.get(selectors.navigationLinks).contains('Forum');
|
||||
cy.get(selectors.navigationLinks).contains('Curriculum').click();
|
||||
cy.url().should('include', '/learn');
|
||||
cy.get(selectors.navigationLinks).contains('Curriculum');
|
||||
cy.get(selectors.navigationLinks).contains('Forum');
|
||||
cy.get(selectors.navigationLinks).contains('Radio');
|
||||
}
|
||||
);
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
@@ -0,0 +1,77 @@
|
||||
/* global cy */
|
||||
|
||||
const search = query => {
|
||||
cy.get('.ais-SearchBox').within(() => {
|
||||
cy.get('input').type(query);
|
||||
});
|
||||
|
||||
cy.wait(300);
|
||||
};
|
||||
|
||||
const clear = () => {
|
||||
cy.get('.ais-SearchBox').within(() => {
|
||||
cy.get('input').clear();
|
||||
});
|
||||
};
|
||||
|
||||
describe('Search bar', () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
clear();
|
||||
});
|
||||
|
||||
it('Should render properly', () => {
|
||||
cy.get('.ais-SearchBox').should('be.visible');
|
||||
});
|
||||
|
||||
it('Should accept input and display hits', () => {
|
||||
search('freeCodeCamp');
|
||||
|
||||
cy.get('.ais-Hits-list').children().should('to.have.length.of.at.least', 1);
|
||||
});
|
||||
|
||||
it('Should clear hits when input is cleared', () => {
|
||||
search('freeCodeCamp');
|
||||
|
||||
cy.get('.ais-Hits-list').children().should('to.have.length.of.at.least', 1);
|
||||
|
||||
clear();
|
||||
|
||||
cy.get('div.ais-Hits').should('not.exist');
|
||||
});
|
||||
|
||||
it('Should show up to 8 hits when height >= 768px', () => {
|
||||
cy.viewport(1300, 768);
|
||||
|
||||
search('freeCodeCamp');
|
||||
|
||||
cy.get('.ais-Hits-list').children().should('to.have.length.of', 8);
|
||||
});
|
||||
|
||||
it('Should show up to 5 hits when height < 768px', () => {
|
||||
cy.viewport(1300, 767);
|
||||
|
||||
search('freeCodeCamp');
|
||||
|
||||
cy.get('.ais-Hits-list').children().should('to.have.length.of', 5);
|
||||
});
|
||||
|
||||
it('Should show no hits for queries that do not exist in the Algolia index', () => {
|
||||
search('testtttt');
|
||||
|
||||
cy.get('.ais-Hits-list').children().should('to.have.length.of', 0);
|
||||
|
||||
cy.contains('No tutorials found');
|
||||
});
|
||||
|
||||
it('Should not redirect to the News search page if there are no hits', () => {
|
||||
search('testtttt');
|
||||
|
||||
cy.get('.ais-SearchBox-form').submit();
|
||||
|
||||
cy.url('/');
|
||||
});
|
||||
});
|
@@ -0,0 +1,65 @@
|
||||
/* global cy */
|
||||
|
||||
const selectors = {
|
||||
donateSupport: {
|
||||
firstTitle: '.donate-support h4:first-of-type b',
|
||||
secondTitle: '.donate-support h4:last-of-type b',
|
||||
firstText: '.donate-support p:first-of-type',
|
||||
secondText: '.donate-support p:last-of-type',
|
||||
link: '.donate-support a'
|
||||
}
|
||||
};
|
||||
|
||||
describe('Donate page', () => {
|
||||
before(() => {
|
||||
cy.clearCookies();
|
||||
cy.exec('npm run seed');
|
||||
cy.login();
|
||||
cy.visit('/donate');
|
||||
});
|
||||
|
||||
it('Should render', () => {
|
||||
cy.title().should('eq', 'Support our nonprofit | freeCodeCamp.org');
|
||||
});
|
||||
|
||||
it('Should display default amount and duration', () => {
|
||||
cy.contains('Confirm your donation of $5 / month:').should('be.visible');
|
||||
});
|
||||
|
||||
it('Should have support section', () => {
|
||||
cy.contains(
|
||||
'Want to make a bigger one-time donation, mail us a check, or give in other ways?'
|
||||
).should('be.visible');
|
||||
});
|
||||
|
||||
it('Support section should have support text', () => {
|
||||
cy.contains(
|
||||
selectors.donateSupport.firstTitle,
|
||||
'Want to make a bigger one-time donation, mail us a check, or give in other ways?'
|
||||
);
|
||||
cy.contains(
|
||||
selectors.donateSupport.secondTitle,
|
||||
'Need help with your current or past donations?'
|
||||
);
|
||||
cy.contains(
|
||||
selectors.donateSupport.firstText,
|
||||
"Here are many other ways you can support our non-profit's mission."
|
||||
);
|
||||
cy.contains(
|
||||
selectors.donateSupport.secondText,
|
||||
'Forward a copy of your donation receipt to donors@freecodecamp.org and tell us how we can help.'
|
||||
);
|
||||
});
|
||||
|
||||
it('Support section should have donation link', () => {
|
||||
cy.get(selectors.donateSupport.link).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'https://www.freecodecamp.org/news/how-to-donate-to-free-code-camp'
|
||||
);
|
||||
});
|
||||
|
||||
it('Donor alert should not be visible for non-donor', () => {
|
||||
cy.get('.alert-info').should('not.exist');
|
||||
});
|
||||
});
|
@@ -0,0 +1,45 @@
|
||||
/* global cy */
|
||||
|
||||
const selectors = {
|
||||
donateAlert: {
|
||||
firstText: '.alert-info p:first-child',
|
||||
secondText: '.alert-info p:last-child',
|
||||
link: '.alert-info a'
|
||||
}
|
||||
};
|
||||
|
||||
describe('Donate page', () => {
|
||||
before(() => {
|
||||
cy.clearCookies();
|
||||
cy.exec('npm run seed -- --donor');
|
||||
cy.login();
|
||||
cy.visit('/donate');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.exec('npm run seed');
|
||||
});
|
||||
|
||||
it('Donor alert should be visible for donor', () => {
|
||||
cy.get('.alert-info').should('be.visible');
|
||||
});
|
||||
|
||||
it('Donor should see alert message', () => {
|
||||
cy.contains(
|
||||
selectors.donateAlert.firstText,
|
||||
'Thank you for being a supporter of freeCodeCamp. You currently have a recurring donation.'
|
||||
);
|
||||
cy.contains(
|
||||
selectors.donateAlert.lastText,
|
||||
"Want to make a bigger one-time donation, mail us a check, or give in other ways? Here are many other ways you can support our non-profit's mission."
|
||||
);
|
||||
});
|
||||
|
||||
it('Donor alert section should have donation link', () => {
|
||||
cy.get(selectors.donateAlert.link).should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'https://www.freecodecamp.org/news/how-to-donate-to-free-code-camp'
|
||||
);
|
||||
});
|
||||
});
|
@@ -0,0 +1,45 @@
|
||||
/* global cy */
|
||||
|
||||
describe('Donate page', () => {
|
||||
before(() => {
|
||||
cy.clearCookies();
|
||||
cy.exec('npm run seed');
|
||||
cy.login();
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.exec('npm run seed');
|
||||
});
|
||||
|
||||
const projects = [
|
||||
'tribute-page',
|
||||
'survey-form',
|
||||
'product-landing-page',
|
||||
'technical-documentation-page',
|
||||
'personal-portfolio-webpage'
|
||||
];
|
||||
|
||||
it('Should be able to submit projects', () => {
|
||||
const submitProject = str => {
|
||||
cy.visit(
|
||||
`/learn/responsive-web-design/responsive-web-design-projects/build-a-${str}`
|
||||
);
|
||||
cy.get('#dynamic-front-end-form')
|
||||
.get('#solution')
|
||||
.type('https://codepen.io/camperbot/full/oNvPqqo', {
|
||||
force: true
|
||||
});
|
||||
|
||||
cy.contains("I've completed this challenge").click();
|
||||
cy.contains('Submit and go to next challenge').click();
|
||||
};
|
||||
|
||||
projects.forEach(project => submitProject(project));
|
||||
});
|
||||
|
||||
it('Should have a pop up modal', () => {
|
||||
cy.contains(
|
||||
'Nicely done. You just completed Responsive Web Design Projects.'
|
||||
);
|
||||
});
|
||||
});
|
85
cypress/integration/main-tests/learn/index.js
Normal file
85
cypress/integration/main-tests/learn/index.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/* global cy expect */
|
||||
|
||||
const selectors = {
|
||||
challengeMap: "[data-test-label='learn-curriculum-map']"
|
||||
};
|
||||
|
||||
const locations = {
|
||||
index: '/learn'
|
||||
};
|
||||
|
||||
const superBlockNames = [
|
||||
'Responsive Web Design Certification',
|
||||
'JavaScript Algorithms and Data Structures Certification',
|
||||
'Front End Development Libraries Certification',
|
||||
'Data Visualization Certification',
|
||||
'APIs and Microservices Certification',
|
||||
'Quality Assurance Certification',
|
||||
'Scientific Computing with Python Certification',
|
||||
'Data Analysis with Python Certification',
|
||||
'Information Security Certification',
|
||||
'Machine Learning with Python Certification',
|
||||
'Coding Interview Prep (Thousands of hours of challenges)'
|
||||
];
|
||||
|
||||
describe('Learn Landing page (not logged in)', () => {
|
||||
it('Should render', () => {
|
||||
cy.visit(locations.index);
|
||||
|
||||
cy.title().should(
|
||||
'eq',
|
||||
'Learn to Code — For Free — Coding Courses for Busy People'
|
||||
);
|
||||
});
|
||||
|
||||
it('Has the correct heading for an unauthenticated User', () => {
|
||||
cy.visit(locations.index);
|
||||
|
||||
cy.contains('h1', "Welcome to freeCodeCamp's curriculum.");
|
||||
});
|
||||
|
||||
it('Should render a curriculum map', () => {
|
||||
cy.document().then(document => {
|
||||
const superBlocks = document.querySelectorAll(
|
||||
`${selectors.challengeMap} > li > a`
|
||||
);
|
||||
expect(superBlocks).to.have.length(11);
|
||||
|
||||
superBlocks.forEach((superBlock, idx) => {
|
||||
expect(superBlock.innerText).to.have.string(superBlockNames[idx]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Quotes', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
cy.contains("Get started (it's free)").click();
|
||||
});
|
||||
|
||||
it('Should show a quote', () => {
|
||||
cy.get('blockquote').within(() => {
|
||||
cy.get('q').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should show quote author', () => {
|
||||
cy.get('blockquote').within(() => {
|
||||
cy.get('cite').should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Superblocks and Blocks', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
cy.contains("Get started (it's free)").click();
|
||||
});
|
||||
|
||||
it('Has all superblocks visible', () => {
|
||||
cy.wrap(superBlockNames.slice(1)).each(name => {
|
||||
cy.contains(name).should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
54
cypress/integration/main-tests/learn/redirects/challenges.js
Normal file
54
cypress/integration/main-tests/learn/redirects/challenges.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/* global cy expect */
|
||||
|
||||
const locations = {
|
||||
chalSuper: '/challenges/responsive-web-design/',
|
||||
chalBlock: '/challenges/responsive-web-design/basic-html-and-html5',
|
||||
chalChallenge:
|
||||
// eslint-disable-next-line max-len
|
||||
'/challenges/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements',
|
||||
learnSuper: '/learn/responsive-web-design',
|
||||
learnBlock: '/learn/responsive-web-design/basic-html-and-html5',
|
||||
learnChallenge:
|
||||
// eslint-disable-next-line max-len
|
||||
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements'
|
||||
};
|
||||
|
||||
describe('challenges/superblock redirect', function () {
|
||||
it('redirects to learn/superblock', () => {
|
||||
cy.visit(locations.chalSuper);
|
||||
|
||||
cy.title().should(
|
||||
'eq',
|
||||
'Responsive Web Design Certification | freeCodeCamp.org'
|
||||
);
|
||||
cy.location().should(loc => {
|
||||
expect(loc.pathname).to.eq(locations.learnSuper);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('challenges/superblock/block redirect', function () {
|
||||
it('redirects to learn/superblock/block', () => {
|
||||
cy.visit(locations.chalBlock);
|
||||
|
||||
cy.title().should('eq', 'Basic HTML and HTML5 | freeCodeCamp.org');
|
||||
cy.location().should(loc => {
|
||||
expect(loc.pathname).to.eq(locations.learnBlock);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('challenges/superblock/block/challenge redirect', function () {
|
||||
it('redirects to learn/superblock/block/challenge', () => {
|
||||
cy.visit(locations.chalChallenge);
|
||||
|
||||
cy.title().should(
|
||||
'eq',
|
||||
// eslint-disable-next-line max-len
|
||||
'Learn Basic HTML and HTML5: Say Hello to HTML Elements | freeCodeCamp.org'
|
||||
);
|
||||
cy.location().should(loc => {
|
||||
expect(loc.pathname).to.eq(locations.learnChallenge);
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,81 @@
|
||||
/* global cy */
|
||||
|
||||
const selectors = {
|
||||
tableOfContents: '.intro-toc',
|
||||
warningMessage: '.flash-message-enter-active'
|
||||
};
|
||||
|
||||
const locations = {
|
||||
index: '/learn/responsive-web-design/basic-css/'
|
||||
};
|
||||
|
||||
const lessonNames = [
|
||||
'Change the Color of Text',
|
||||
'Use CSS Selectors to Style Elements',
|
||||
'Use a CSS Class to Style an Element',
|
||||
'Style Multiple Elements with a CSS Class',
|
||||
'Change the Font Size of an Element',
|
||||
'Set the Font Family of an Element',
|
||||
'Import a Google Font',
|
||||
'Specify How Fonts Should Degrade',
|
||||
'Size Your Images',
|
||||
'Add Borders Around Your Elements',
|
||||
'Add Rounded Corners with border-radius',
|
||||
'Make Circular Images with a border-radius',
|
||||
'Give a Background Color to a div Element',
|
||||
'Set the id of an Element',
|
||||
'Use an id Attribute to Style an Element',
|
||||
'Adjust the Padding of an Element',
|
||||
'Adjust the Margin of an Element',
|
||||
'Add a Negative Margin to an Element',
|
||||
'Add Different Padding to Each Side of an Element',
|
||||
'Add Different Margins to Each Side of an Element',
|
||||
'Use Clockwise Notation to Specify the Padding of an Element',
|
||||
'Use Clockwise Notation to Specify the Margin of an Element',
|
||||
'Use Attribute Selectors to Style Elements',
|
||||
'Understand Absolute versus Relative Units',
|
||||
'Style the HTML Body Element',
|
||||
'Inherit Styles from the Body Element',
|
||||
'Prioritize One Style Over Another',
|
||||
'Override Styles in Subsequent CSS',
|
||||
'Override Class Declarations by Styling ID Attributes',
|
||||
'Override Class Declarations with Inline Styles',
|
||||
'Override All Other Styles by using Important',
|
||||
'Use Hex Code for Specific Colors',
|
||||
'Use Hex Code to Mix Colors',
|
||||
'Use Abbreviated Hex Code',
|
||||
'Use RGB values to Color Elements',
|
||||
'Use RGB to Mix Colors',
|
||||
'Use CSS Variables to change several elements at once',
|
||||
'Create a custom CSS Variable',
|
||||
'Use a custom CSS Variable',
|
||||
'Attach a Fallback value to a CSS Variable',
|
||||
'Improve Compatibility with Browser Fallbacks',
|
||||
'Inherit CSS Variables',
|
||||
'Change a variable for a specific area',
|
||||
'Use a media query to change a variable'
|
||||
];
|
||||
|
||||
const warningMessage =
|
||||
'Note: Some browser extensions may interfere with elements on the page. ' +
|
||||
'If the tests fail, try disabling your extensions for the most reliable ' +
|
||||
'experience.';
|
||||
|
||||
describe('Basic Css Introduction page', function () {
|
||||
it('renders', () => {
|
||||
cy.visit(locations.index);
|
||||
|
||||
cy.title().should('eq', 'Basic CSS | freeCodeCamp.org');
|
||||
});
|
||||
|
||||
it('renders a warning user about extensions', () => {
|
||||
cy.visit(locations.index);
|
||||
cy.get(selectors.warningMessage).contains(warningMessage);
|
||||
});
|
||||
|
||||
it('renders a lesson index', () => {
|
||||
lessonNames.forEach(name => {
|
||||
cy.get(selectors.tableOfContents).contains('span', name);
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,41 @@
|
||||
/* global cy */
|
||||
|
||||
const selectors = {
|
||||
firstBlock: '.block-ui > .block:nth-child(1) > .map-title'
|
||||
};
|
||||
|
||||
describe('Certification intro page', () => {
|
||||
before(() => {
|
||||
cy.clearCookies();
|
||||
cy.login();
|
||||
cy.visit('/learn/responsive-web-design');
|
||||
});
|
||||
|
||||
it('Should render', () => {
|
||||
cy.title().should(
|
||||
'eq',
|
||||
'Responsive Web Design Certification | freeCodeCamp.org'
|
||||
);
|
||||
});
|
||||
|
||||
it('Should have certification intro text', () => {
|
||||
cy.contains(
|
||||
"In this Responsive Web Design Certification, you'll learn the languages that developers use to build webpages"
|
||||
).should('be.visible');
|
||||
});
|
||||
|
||||
it('First block should be expanded', () => {
|
||||
cy.contains('Say Hello to HTML Elements').should('be.visible');
|
||||
});
|
||||
|
||||
it('Second block should be closed', () => {
|
||||
cy.contains('Change the Color of Text').should('not.exist');
|
||||
});
|
||||
|
||||
it('Block should handle toggle clicks correctly', () => {
|
||||
cy.get(selectors.firstBlock).click();
|
||||
cy.contains('Say Hello to HTML Elements').should('not.exist');
|
||||
cy.get(selectors.firstBlock).click();
|
||||
cy.contains('Say Hello to HTML Elements').should('be.visible');
|
||||
});
|
||||
});
|
59
cypress/integration/main-tests/settings/certifications.js
Normal file
59
cypress/integration/main-tests/settings/certifications.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/* global cy expect */
|
||||
|
||||
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', () => {
|
||||
cy.findAllByText('Claim Certification').should($btns => {
|
||||
expect($btns).to.have.length(15);
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
44
cypress/integration/main-tests/settings/email-change.js
Normal file
44
cypress/integration/main-tests/settings/email-change.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/* global cy */
|
||||
describe('Email input field', () => {
|
||||
beforeEach(() => {
|
||||
cy.exec('npm run seed');
|
||||
cy.login();
|
||||
cy.visit('/settings');
|
||||
});
|
||||
|
||||
it('Should be possible to submit the new email', () => {
|
||||
cy.get('[id=new-email]')
|
||||
.type('bar@foo.com')
|
||||
.should('have.attr', 'value', 'bar@foo.com');
|
||||
|
||||
cy.get('[id=confirm-email]')
|
||||
.type('bar@foo.com')
|
||||
.should('have.attr', 'value', 'bar@foo.com');
|
||||
|
||||
cy.get('[id=form-update-email]').within(() => {
|
||||
cy.contains('Save').click();
|
||||
});
|
||||
cy.contains(
|
||||
'Check your email and click the link we sent you to confirm your new email address.'
|
||||
);
|
||||
});
|
||||
|
||||
it('Displays an error message when there are problems with the submitted emails', () => {
|
||||
cy.get('[id=new-email]').type('bar@foo.com');
|
||||
cy.get('[id=confirm-email]').type('foo@bar.com');
|
||||
|
||||
cy.get('[class=help-block]').contains(
|
||||
'Both new email addresses must be the same'
|
||||
);
|
||||
|
||||
cy.get('[id=new-email]').clear().type('foo@bar.com');
|
||||
|
||||
cy.get('[class=help-block]').contains(
|
||||
'This email is the same as your current email'
|
||||
);
|
||||
});
|
||||
|
||||
it('Should be possible to get Quincys weekly email', () => {
|
||||
cy.contains('Yes please').click();
|
||||
});
|
||||
});
|
@@ -0,0 +1,39 @@
|
||||
/* global cy */
|
||||
|
||||
describe('Picture input field', () => {
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.visit('/settings');
|
||||
// Setting aliases here
|
||||
cy.get('input#about-picture').as('pictureInput');
|
||||
});
|
||||
|
||||
it('Should be possible to type', () => {
|
||||
cy.get('@pictureInput')
|
||||
.clear({ force: true })
|
||||
.type('twaha', { force: true })
|
||||
.should('have.attr', 'value', 'twaha');
|
||||
});
|
||||
it('Show an error message if an incorrect url was submitted', () => {
|
||||
cy.get('@pictureInput')
|
||||
.clear({ force: true })
|
||||
.type('https://s3.amazonaws.com/freecodecamp/camper-image', {
|
||||
force: true
|
||||
})
|
||||
.then(() => {
|
||||
cy.contains('URL must link directly to an image file');
|
||||
});
|
||||
});
|
||||
it('Can submit a correct URL', () => {
|
||||
cy.get('@pictureInput')
|
||||
.clear({ force: true })
|
||||
.type(
|
||||
'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png',
|
||||
{
|
||||
force: true
|
||||
}
|
||||
);
|
||||
cy.wait(500);
|
||||
cy.get('#camper-identity > .btn').should('not.be.disabled');
|
||||
});
|
||||
});
|
15
cypress/integration/main-tests/settings/settings.js
Normal file
15
cypress/integration/main-tests/settings/settings.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/* global cy expect */
|
||||
|
||||
describe('Settings', () => {
|
||||
it('should be possible to reset your progress', () => {
|
||||
cy.visit('/');
|
||||
cy.contains("Get started (it's free)").click();
|
||||
cy.visit('/settings');
|
||||
cy.contains('Reset all of my progress').click();
|
||||
cy.contains('Reset everything. I want to start from the beginning').click();
|
||||
cy.location().should(loc => {
|
||||
expect(loc.pathname).to.eq('/');
|
||||
});
|
||||
cy.contains('Your progress has been reset');
|
||||
});
|
||||
});
|
219
cypress/integration/main-tests/settings/username-change.js
Normal file
219
cypress/integration/main-tests/settings/username-change.js
Normal file
@@ -0,0 +1,219 @@
|
||||
/* global cy */
|
||||
|
||||
describe('Username input field', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
cy.contains("Get started (it's free)").click({ force: true });
|
||||
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', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('twaha', { force: true })
|
||||
.should('have.attr', 'value', 'twaha');
|
||||
});
|
||||
|
||||
it('Should show message when validating name', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('twaha', { force: true });
|
||||
|
||||
cy.contains('Validating username...')
|
||||
.should('have.attr', 'role', 'alert')
|
||||
// We are checking for classes here to check for proper styling
|
||||
// This will be replaced with Percy in the future
|
||||
.should('have.class', 'alert alert-info');
|
||||
});
|
||||
|
||||
it('Should show username is available if it is', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('brad', { force: true });
|
||||
|
||||
cy.contains('Username is available')
|
||||
.should('be.visible')
|
||||
.should('have.attr', 'role', 'alert')
|
||||
// We are checking for classes here to check for proper styling
|
||||
// This will be replaced with Percy in the future
|
||||
.should('have.class', 'alert alert-success');
|
||||
});
|
||||
|
||||
it('Should info message if username is available', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('mrugesh', { force: true });
|
||||
|
||||
cy.contains(
|
||||
'Please note, changing your username will also change ' +
|
||||
'the URL to your profile and your certifications.'
|
||||
)
|
||||
.should('be.visible')
|
||||
.should('have.attr', 'role', 'alert')
|
||||
// We are checking for classes here to check for proper styling
|
||||
// This will be replaced with Percy in the future
|
||||
.should('have.class', 'alert alert-info');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
it('Should be able to click the `Save` button if username is avalable', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('oliver', { force: true });
|
||||
|
||||
cy.get('@usernameForm').within(() => {
|
||||
cy.contains('Save').should('not.be.disabled');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should show username is unavailable if it is', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('twaha', { force: true });
|
||||
|
||||
cy.contains('Username not available')
|
||||
.should('be.visible')
|
||||
.should('have.attr', 'role', 'alert')
|
||||
// We are checking for classes here to check for proper styling
|
||||
// This will be replaced with Percy in the future
|
||||
.should('have.class', 'alert alert-warning');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line
|
||||
it('Should not be possible to click the `Save` button if username is unavailable', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('twaha', { force: true });
|
||||
|
||||
cy.contains('Username is available').should('not.exist');
|
||||
cy.contains('Username not available').should('not.exist');
|
||||
cy.contains(
|
||||
'Please note, changing your username will also change ' +
|
||||
'the URL to your profile and your certifications.'
|
||||
).should('not.exist');
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
});
|
||||
|
||||
it('Should not show anything if user types their current name', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('developmentuser', { force: true });
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('Should not be possible to click the `Save` button if user types their current name', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('developmentuser', { force: true });
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
});
|
||||
|
||||
it('Should show warning if username includes invalid character', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('Quincy Larson', { force: true });
|
||||
|
||||
cy.contains('Username "Quincy Larson" contains invalid characters')
|
||||
.should('be.visible')
|
||||
.should('have.attr', 'role', 'alert')
|
||||
// We are checking for classes here to check for proper styling
|
||||
// This will be replaced with Percy in the future
|
||||
.should('have.class', 'alert alert-danger');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('Should not be able to click the `Save` button if username includes invalid character', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('Quincy Larson', { force: true });
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
});
|
||||
|
||||
it('Should change username if `Save` button is clicked', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('quincy', { force: true });
|
||||
|
||||
cy.contains('Username is available');
|
||||
|
||||
cy.get('@usernameForm').contains('Save').click({ force: true });
|
||||
cy.contains('Account Settings for quincy').should('be.visible');
|
||||
|
||||
cy.resetUsername();
|
||||
});
|
||||
|
||||
it('Should show flash message showing username has been updated', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('nhcarrigan', { force: true });
|
||||
cy.contains('Username is available');
|
||||
cy.get('@usernameInput').type('{enter}', { force: true, release: false });
|
||||
|
||||
cy.contains('We have updated your username to nhcarrigan')
|
||||
.should('be.visible')
|
||||
// We are checking for classes here to check for proper styling
|
||||
// This will be replaced with Percy in the future
|
||||
.should(
|
||||
'have.class',
|
||||
'flash-message alert alert-success alert-dismissable'
|
||||
);
|
||||
|
||||
cy.resetUsername();
|
||||
});
|
||||
|
||||
it('Should be able to close the shown flash message', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('bjorno', { force: true });
|
||||
cy.contains('Username is available');
|
||||
cy.get('@usernameInput').type('{enter}', { force: true, release: false });
|
||||
|
||||
cy.contains('We have updated your username to bjorno').within(() => {
|
||||
cy.get('button').click();
|
||||
});
|
||||
|
||||
cy.contains('We have updated your username to bjorno').should('not.exist');
|
||||
|
||||
cy.resetUsername();
|
||||
});
|
||||
|
||||
it('Should change username if enter is pressed', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('symbol', { force: true });
|
||||
cy.contains('Username is available');
|
||||
|
||||
cy.get('@usernameInput').type('{enter}', { force: true, release: false });
|
||||
|
||||
cy.contains('Account Settings for symbol').should('be.visible');
|
||||
|
||||
cy.resetUsername();
|
||||
});
|
||||
it('Should show warning if username includes uppercase characters', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('QuincyLarson', { force: true });
|
||||
|
||||
cy.contains('Username "QuincyLarson" must be lowercase')
|
||||
.should('be.visible')
|
||||
.should('have.attr', 'role', 'alert')
|
||||
.should('have.class', 'alert alert-danger');
|
||||
});
|
||||
|
||||
it('Should not be able to click the `Save` button if username includes uppercase characters', () => {
|
||||
cy.get('@usernameInput')
|
||||
.clear({ force: true })
|
||||
.type('QuincyLarson', { force: true });
|
||||
|
||||
cy.get('@usernameForm').contains('Save').should('be.disabled');
|
||||
});
|
||||
});
|
88
cypress/integration/main-tests/tags.js
Normal file
88
cypress/integration/main-tests/tags.js
Normal file
@@ -0,0 +1,88 @@
|
||||
/* global cy */
|
||||
describe('The Document Metadata', () => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.document();
|
||||
});
|
||||
|
||||
const social = {
|
||||
description: 'Learn to Code — For Free'
|
||||
};
|
||||
|
||||
const challengs = {
|
||||
responsiveWebDesign:
|
||||
'/learn/responsive-web-design/basic-html-and-html5/say-hello-to-html-elements',
|
||||
rosetaCode: '/learn/coding-interview-prep/rosetta-code/100-doors',
|
||||
projectEuler:
|
||||
'/learn/coding-interview-prep/project-euler/problem-1-multiples-of-3-and-5'
|
||||
};
|
||||
|
||||
const scripts = {
|
||||
mathjax: {
|
||||
selector: 'body script[id="mathjax"]',
|
||||
src: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-AMS_HTML'
|
||||
}
|
||||
};
|
||||
it('landing page has correct <meta> for description', () => {
|
||||
cy.get('head meta[name="description"]').should(
|
||||
'have.attr',
|
||||
'content',
|
||||
'Learn to Code — For Free'
|
||||
);
|
||||
});
|
||||
it('landing page has correct <meta> for og title', () => {
|
||||
cy.get('head meta[name="og:title"]').should(
|
||||
'have.attr',
|
||||
'content',
|
||||
'freeCodeCamp.org'
|
||||
);
|
||||
});
|
||||
it('landing page has correct <meta> for og description', () => {
|
||||
cy.get('head meta[name="og:description"]').should(
|
||||
'have.attr',
|
||||
'content',
|
||||
social.description
|
||||
);
|
||||
});
|
||||
it('landing page has correct <meta> for twitter title', () => {
|
||||
cy.get('head meta[name="twitter:title"]').should(
|
||||
'have.attr',
|
||||
'content',
|
||||
'freeCodeCamp.org'
|
||||
);
|
||||
});
|
||||
it('landing page has correct <meta>for twitter description', () => {
|
||||
cy.get('head meta[name="twitter:description"]').should(
|
||||
'have.attr',
|
||||
'content',
|
||||
social.description
|
||||
);
|
||||
});
|
||||
it('landing page should not have mathjax body script', () => {
|
||||
cy.reload();
|
||||
cy.get(scripts.mathjax.selector).should('not.exist');
|
||||
});
|
||||
it('responsive webdesign challenges should not have mathjax body script', () => {
|
||||
cy.visit(challengs.responsiveWebDesign);
|
||||
cy.reload();
|
||||
cy.get(scripts.mathjax.selector).should('not.exist');
|
||||
});
|
||||
it('project euler challenges should have mathjax body script', () => {
|
||||
cy.visit(challengs.projectEuler);
|
||||
cy.reload();
|
||||
cy.get(scripts.mathjax.selector).should(
|
||||
'have.attr',
|
||||
'src',
|
||||
scripts.mathjax.src
|
||||
);
|
||||
});
|
||||
it('rosetta code challenges should have mathjax body script', () => {
|
||||
cy.visit(challengs.projectEuler);
|
||||
cy.reload();
|
||||
cy.get(scripts.mathjax.selector).should(
|
||||
'have.attr',
|
||||
'src',
|
||||
scripts.mathjax.src
|
||||
);
|
||||
});
|
||||
});
|
42
cypress/integration/main-tests/top-contributor.js
Normal file
42
cypress/integration/main-tests/top-contributor.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/* global cy */
|
||||
|
||||
describe('Top contributor in user profile', () => {
|
||||
before(() => {
|
||||
cy.clearCookies();
|
||||
cy.exec('npm run seed -- --top-contributor');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
cy.exec('npm run seed');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.login();
|
||||
cy.contains('Profile').click({ force: true });
|
||||
|
||||
// The following line is only required if you want to test it in development
|
||||
// cy.contains('Preview custom 404 page').click();
|
||||
});
|
||||
|
||||
it('Should show `Top Contributor` text with badge', () => {
|
||||
cy.contains('Top Contributor')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.contains('Top Contributor').should('be.visible');
|
||||
cy.get('svg').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
it('Should take user to `Top Contributor` page when `Top Contributor` gets clicked', () => {
|
||||
cy.contains('Top Contributor').should(
|
||||
'have.attr',
|
||||
'href',
|
||||
'/top-contributors'
|
||||
);
|
||||
});
|
||||
|
||||
it('Should show years when it was achieved', () => {
|
||||
cy.contains('2017, 2018 and 2019').should('be.visible');
|
||||
});
|
||||
});
|
25
cypress/integration/main-tests/user/report-user.js
Normal file
25
cypress/integration/main-tests/user/report-user.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/* global cy expect */
|
||||
|
||||
describe('Report User', () => {
|
||||
beforeEach(() => {
|
||||
cy.exec('npm run seed');
|
||||
cy.login();
|
||||
});
|
||||
it('should be possible to report a user from their profile page', () => {
|
||||
// Since going to a user page intially generates a 404, we have to ignore
|
||||
// status codes on that request
|
||||
cy.visit('/twaha', { failOnStatusCode: false });
|
||||
// The following line is only required if you want to test it in development
|
||||
// cy.contains('Preview custom 404 page').click();
|
||||
cy.contains("Flag This User's Account for Abuse").click();
|
||||
cy.contains("Do you want to report twaha's portfolio for abuse?");
|
||||
cy.get('[id=report-user-textarea]').type('Some details');
|
||||
cy.contains('Submit the report').click();
|
||||
cy.location().should(loc => {
|
||||
expect(loc.pathname).to.eq('/learn');
|
||||
});
|
||||
cy.contains('A report was sent to the team with foo@bar.com in copy');
|
||||
});
|
||||
});
|
||||
|
||||
// A report was sent to the team with foo@bar.com in copy
|
Reference in New Issue
Block a user