diff --git a/cypress/integration/settings/settings.js b/cypress/integration/settings/settings.js new file mode 100644 index 0000000000..5369920a60 --- /dev/null +++ b/cypress/integration/settings/settings.js @@ -0,0 +1,26 @@ +/* global cy expect */ + +describe('Settings', () => { + beforeEach(() => { + cy.visit('/'); + cy.contains("Get started (it's free)").click({ force: true }); + cy.visit('/settings'); + }); + + describe('The `Sign me out of freeCodeCamp` button works properly', () => { + it('Should get rendered properly', () => { + cy.contains('Sign me out of freeCodeCamp') + .should('be.visible') + // We are checking for classes here to check for proper styling + // This will be replaces with Percy in the future + .should('have.class', 'btn-invert btn btn-lg btn-primary btn-block'); + }); + + it('Should take to the landing page when clicked', () => { + cy.contains('Sign me out of freeCodeCamp').click({ force: true }); + cy.location().should(loc => { + expect(loc.pathname).to.eq('/'); + }); + }); + }); +}); diff --git a/cypress/integration/settings/username-change.js b/cypress/integration/settings/username-change.js new file mode 100644 index 0000000000..c8ad305557 --- /dev/null +++ b/cypress/integration/settings/username-change.js @@ -0,0 +1,213 @@ +/* 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.be.visible'); + cy.contains('Username not available.').should('not.be.visible'); + cy.contains( + 'Please note, changing your username will also change ' + + 'the URL to your profile and your certifications.' + ).should('not.be.visible'); + + 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 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.be.visible' + ); + + 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(); + }); +}); diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 02f383069c..46b8f46724 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -37,3 +37,18 @@ Cypress.Commands.add('login', () => { cy.visit('/'); cy.contains("Get started (it's free)").click({ force: true }); }); + +Cypress.Commands.add('resetUsername', () => { + cy.login(); + cy.visit('/settings'); + + cy.get('@usernameInput') + .clear({ force: true }) + .type('developmentuser', { force: true }); + + cy.contains('Username is available.'); + + cy.get('@usernameInput').type('{enter}', { force: true, release: false }); + + cy.contains('Account Settings for developmentuser').should('be.visible'); +}); diff --git a/tools/scripts/seed/seedAuthUser.js b/tools/scripts/seed/seedAuthUser.js index a75ae09b1d..cf8ccdd69b 100644 --- a/tools/scripts/seed/seedAuthUser.js +++ b/tools/scripts/seed/seedAuthUser.js @@ -9,7 +9,7 @@ const envVariables = process.argv; const log = debug('fcc:tools:seedLocalAuthUser'); const { MONGOHQ_URL } = process.env; -const defaulUserImage = require('../../../config/misc').defaulUserImage; +const defaultUserImage = require('../../../config/misc').defaultUserImage; function handleError(err, client) { if (err) { @@ -26,10 +26,7 @@ function handleError(err, client) { } } -MongoClient.connect(MONGOHQ_URL, { useNewUrlParser: true }, function( - err, - client -) { +MongoClient.connect(MONGOHQ_URL, { useNewUrlParser: true }, (err, client) => { handleError(err, client); log('Connected successfully to mongo'); @@ -37,73 +34,140 @@ MongoClient.connect(MONGOHQ_URL, { useNewUrlParser: true }, function( const db = client.db('freecodecamp'); const user = db.collection('user'); - user.deleteOne({ _id: ObjectId('5bd30e0f1caf6ac3ddddddb5') }, err => { - handleError(err, client); + user.deleteMany( + { + _id: { + $in: [ + ObjectId('5bd30e0f1caf6ac3ddddddb5'), + ObjectId('5bd30e0f1caf6ac3ddddddb9') + ] + } + }, + err => { + handleError(err, client); - try { - user.insertOne({ - _id: ObjectId('5bd30e0f1caf6ac3ddddddb5'), - email: 'foo@bar.com', - emailVerified: true, - progressTimestamps: [], - isBanned: false, - isCheater: false, - username: 'developmentuser', - about: '', - name: 'Development User', - location: '', - picture: defaulUserImage, - acceptedPrivacyTerms: true, - sendQuincyEmail: false, - currentChallengeId: '', - isHonest: false, - isFrontEndCert: false, - isDataVisCert: false, - isBackEndCert: false, - isFullStackCert: false, - isRespWebDesignCert: false, - is2018DataVisCert: false, - isFrontEndLibsCert: false, - isJsAlgoDataStructCert: false, - isApisMicroservicesCert: false, - isInfosecQaCert: false, - isQaCertV7: false, - isInfosecCertV7: false, - is2018FullStackCert: false, - isSciCompPyCertV7: false, - isDataAnalysisPyCertV7: false, - isMachineLearningPyCertV7: false, - completedChallenges: [], - portfolio: [], - yearsTopContributor: envVariables.includes('--top-contributor') - ? ['2017', '2018', '2019'] - : [], - rand: 0.6126749173148205, - theme: 'default', - profileUI: { - isLocked: true, - showAbout: false, - showCerts: false, - showDonation: false, - showHeatMap: false, - showLocation: false, - showName: false, - showPoints: false, - showPortfolio: false, - showTimeLine: false - }, - badges: { - coreTeam: [] - }, - isDonating: envVariables.includes('--donor'), - emailAuthLinkTTL: null, - emailVerifyTTL: null - }); - } catch (e) { - handleError(e, client); - } finally { - log('local auth user seed complete'); - client.close(); + try { + user.insertOne({ + _id: ObjectId('5bd30e0f1caf6ac3ddddddb5'), + email: 'foo@bar.com', + emailVerified: true, + progressTimestamps: [], + isBanned: false, + isCheater: false, + username: 'developmentuser', + about: '', + name: 'Development User', + location: '', + picture: defaultUserImage, + acceptedPrivacyTerms: true, + sendQuincyEmail: false, + currentChallengeId: '', + isHonest: false, + isFrontEndCert: false, + isDataVisCert: false, + isBackEndCert: false, + isFullStackCert: false, + isRespWebDesignCert: false, + is2018DataVisCert: false, + isFrontEndLibsCert: false, + isJsAlgoDataStructCert: false, + isApisMicroservicesCert: false, + isInfosecQaCert: false, + isQaCertV7: false, + isInfosecCertV7: false, + is2018FullStackCert: false, + isSciCompPyCertV7: false, + isDataAnalysisPyCertV7: false, + isMachineLearningPyCertV7: false, + completedChallenges: [], + portfolio: [], + yearsTopContributor: envVariables.includes('--top-contributor') + ? ['2017', '2018', '2019'] + : [], + rand: 0.6126749173148205, + theme: 'default', + profileUI: { + isLocked: true, + showAbout: false, + showCerts: false, + showDonation: false, + showHeatMap: false, + showLocation: false, + showName: false, + showPoints: false, + showPortfolio: false, + showTimeLine: false + }, + badges: { + coreTeam: [] + }, + isDonating: envVariables.includes('--donor'), + emailAuthLinkTTL: null, + emailVerifyTTL: null + }); + + user.insertOne({ + _id: ObjectId('5bd30e0f1caf6ac3ddddddb9'), + email: 'bar@bar.com', + emailVerified: true, + progressTimestamps: [], + isBanned: false, + isCheater: false, + username: 'twaha', + about: '', + name: 'Development User', + location: '', + picture: defaultUserImage, + acceptedPrivacyTerms: true, + sendQuincyEmail: false, + currentChallengeId: '', + isHonest: false, + isFrontEndCert: false, + isDataVisCert: false, + isBackEndCert: false, + isFullStackCert: false, + isRespWebDesignCert: false, + is2018DataVisCert: false, + isFrontEndLibsCert: false, + isJsAlgoDataStructCert: false, + isApisMicroservicesCert: false, + isInfosecQaCert: false, + isQaCertV7: false, + isInfosecCertV7: false, + is2018FullStackCert: false, + isSciCompPyCertV7: false, + isDataAnalysisPyCertV7: false, + isMachineLearningPyCertV7: false, + completedChallenges: [], + portfolio: [], + yearsTopContributor: [], + rand: 0.6126749173148205, + theme: 'default', + profileUI: { + isLocked: true, + showAbout: false, + showCerts: false, + showDonation: false, + showHeatMap: false, + showLocation: false, + showName: false, + showPoints: false, + showPortfolio: false, + showTimeLine: false + }, + badges: { + coreTeam: [] + }, + isDonating: false, + emailAuthLinkTTL: null, + emailVerifyTTL: null + }); + } catch (e) { + handleError(e, client); + } finally { + log('local auth user seed complete'); + client.close(); + } } - }); + ); });