Skip to content
Commits on Source (63)
import generateRandomId from '../../support/utilities';
/**
* @author Ben Hayward
* @create date 2019-08-09 14:42:51
......@@ -6,6 +8,9 @@
*/
context('Comment Threads', () => {
const testUsername = generateRandomId();
const testPassword = generateRandomId() + 'rR.7';
const testMessage = {
1: 'test tier 1',
2: 'test tier 2',
......@@ -17,8 +22,6 @@ context('Comment Threads', () => {
const deletePostButton = ".m-modal-confirm-buttons > button:contains('Delete')";
const postCommentButton = 'm-comment__poster > div > div.minds-body > div > div > a.m-post-button';
const thumbsUpCounters = '.m-comment__toolbar > div > minds-button-thumbs-up > a > span';
const thumbsDownCounters = '.m-comment__toolbar > div > minds-button-thumbs-down > a > span';
// pass in tier / tree depth.
const replyButton = `minds-activity:first .m-comment__toolbar > div > span`;
......@@ -26,6 +29,11 @@ context('Comment Threads', () => {
const commentInput = `minds-activity:first m-text-input--autocomplete-container > minds-textarea > div`;
const commentContent = `minds-activity:first m-comments__tree .m-comment__bubble > p`;
const thumbsUpCounters = '[data-cy=data-minds-thumbs-up-counter]' //'minds-button-thumbs-up > a > span';
const thumbsDownCounters = '[data-cy=data-minds-thumbs-down-counter]';
const thumbsUpButton = '[data-cy=data-minds-thumbs-up-button]'
const thumbsDownButton = '[data-cy=data-minds-thumbs-down-button]'
before(() => {
//make a post new.
cy.getCookie('minds_sess')
......@@ -44,6 +52,10 @@ context('Comment Threads', () => {
beforeEach(()=> {
cy.preserveCookies();
cy.server();
cy.route('GET', '**/api/v2/comments/**').as('commentsOpen');
cy.route('POST', '**/api/v1/comments/**').as('postComment');
cy.route('PUT', '**/api/v1/thumbs/**').as('thumbsPut');
});
after(() => {
......@@ -63,13 +75,21 @@ context('Comment Threads', () => {
cy.get(commentContent).contains(testMessage[1]);
//Add the second level of comments
cy.get(replyButton).click();
cy.get(replyButton)
.click()
.wait('@commentsOpen')
.then(xhr => {
expect(xhr.status).to.equal(200);
});
cy.get(commentInput)
.first()
.type(testMessage[2]);
cy.get(postCommentButton)
.first()
.click();
cy.get(commentContent).contains(testMessage[2]);
......@@ -78,30 +98,108 @@ context('Comment Threads', () => {
.find('m-comments__tree m-comments__thread m-comment')
.find('m-comments__thread m-comment:nth-child(2) .m-comment__toolbar > div > span')
.last()
.click();
.click()
.wait('@commentsOpen')
.then(xhr => {
expect(xhr.status).to.equal(200);
});
cy.get(commentInput)
.first()
.type(testMessage[3]);
cy.get(postCommentButton)
.first()
.click();
cy.get(commentContent).contains(testMessage[3]);
//click thumbs up and down
cy.get('.m-comment__toolbar')
.find('minds-button-thumbs-up')
.click({multiple: true});
cy.get(commentContent).contains(testMessage[3]);
// Waiting on component init here.
// If still not fully loaded will not break,
// but may mean some of the buttons aren't tested.
cy.wait(1000);
// scope further get requests down to within the comments toolbar
// avoids clicking thumbs in activity feed.
cy.get('.m-comment__toolbar').within(($list) => {
cy.get('.m-comment__toolbar')
.find('minds-button-thumbs-down')
// thumbs up and down
cy.get(thumbsUpButton)
.click({multiple: true});
// check the values
cy.get(thumbsDownButton)
.click({multiple: true});
// check counters
cy.get(thumbsUpCounters)
.each((counter) => expect(counter.context.innerHTML).to.eql('1'));
.each((counter) => {
expect(counter[0].innerHTML).to.eql('1');
});
});
cy.get(thumbsDownCounters)
.each((counter) => expect(counter.context.innerHTML).to.eql('1'));
.each((counter) => {
expect(counter[0].innerHTML).to.eql('1');
});
});
it('should allow the user to make a mature comment', () => {
// type message
cy.get('minds-textarea')
.last()
.type("naughty message");
// click mature
cy.get('.m-mature-button')
.last()
.click();
// post and await response
cy.get('.m-post-button')
.last()
.click()
.wait('@postComment')
.then(xhr => {
expect(xhr.status).to.equal(200);
});
// Making sure we don't act upon other comments
cy.get('.m-comment__bubble').parent().within($list => {
cy.contains('naughty message')
.should('not.have.class', 'm-mature-text');
cy.get('.m-redButton')
.click();
cy.contains('naughty message')
.should('have.class', 'm-mature-text');
});
// get share link
cy.get(postMenu).click();
cy.contains('Share').click();
// store share link
cy.get('.m-share__copyableLinkText')
.invoke('val')
.then(val => {
// log out
cy.logout();
// visit link
cy.visit(val);
// assert toggle works.
cy.contains('naughty message')
.should('have.class', 'm-mature-text');
cy.get('.m-mature-text-toggle')
.click();
cy.contains('naughty message')
.should('not.have.class', 'm-mature-text');
});
});
})
import generateRandomId from '../../support/utilities';
const groupId = generateRandomId();
context('Groups', () => {
before(() => {
cy.getCookie('minds_sess')
......@@ -25,7 +29,7 @@ context('Groups', () => {
cy.uploadFile('minds-banner #file', '../fixtures/international-space-station-1776401_1920.jpg', 'image/jpg');
// add a name
cy.get('.m-group-info-name > input').type('test');
cy.get('.m-group-info-name > input').type(groupId);
// add a description
cy.get('.m-group-info-brief-description > textarea').type('This is a test');
......@@ -41,15 +45,20 @@ context('Groups', () => {
cy.get('.m-groups-save > button').contains('Create').click();
cy.route("POST", "**/api/v1/groups/group/*/banner*").as("postBanner");
cy.wait('@postGroup').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
}).wait('@postBanner').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
// get current groups count of sidebar
cy.get('.m-groupSidebarMarkers__list').children().its('length').then((size) => {
cy.wait('@postGroup').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
}).wait('@postBanner').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
});
//check count changed.
cy.get('.m-groupSidebarMarkers__list').children().should('have.length', size + 1);
});
cy.get('.m-groupInfo__name').contains('test');
cy.get('.m-groupInfo__name').contains(groupId);
cy.get('.m-groupInfo__description').contains('This is a test');
// open settings button
......@@ -58,7 +67,7 @@ context('Groups', () => {
cy.get('minds-groups-settings-button ul.minds-dropdown-menu li:first-child').contains('Edit').click();
// edit name
cy.get('.m-groupInfo__name input').type(' group');
cy.get('.m-groupInfo__name input').type(' edit');
// edit description
cy.get('.m-groupInfo__description textarea').type(' group');
......@@ -68,16 +77,12 @@ context('Groups', () => {
cy.get('minds-groups-settings-button ul.minds-dropdown-menu li:first-child').contains('Save').click();
cy.get('.m-groupInfo__name').contains('test group');
cy.get('.m-groupInfo__name').contains(groupId + ' edit');
cy.get('.m-groupInfo__description').contains('This is a test group');
})
it('should be able to toggle conversation and comment on it', () => {
cy.get("m-group--sidebar-markers li:contains('test group')")
.first()
.click();
cy.contains(groupId).click();
// toggle the conversation
cy.get('.m-groupGrid__right').should('be.visible');
......@@ -98,9 +103,7 @@ context('Groups', () => {
})
it('should post an activity inside the group and record the view when scrolling', () => {
cy.get("m-group--sidebar-markers li:contains('test group')")
.first()
.click();
cy.contains(groupId).click();
cy.server();
cy.route("POST", "**/api/v2/analytics/views/activity/*").as("view");
......@@ -110,7 +113,7 @@ context('Groups', () => {
cy.get('.m-posterActionBar__PostButton').click();
// the activity should show that it was posted in this group
cy.get('.minds-list minds-activity .body a:nth-child(2)').contains('(test group)');
cy.get('.minds-list minds-activity .body a:nth-child(2)').contains(`(${groupId} edit)`);
cy.get('.minds-list minds-activity .m-mature-message-content').contains('This is a post');
......@@ -127,15 +130,25 @@ context('Groups', () => {
});
});
it('should navigate to discovery when Find a Group clicked', () => {
cy.contains('Find a Group').click()
cy.location('pathname')
.should('eq', '/newsfeed/global/top%3Bperiod%3D12h%3Btype%3Dgroups%3Ball%3D1');
});
it('should delete a group', () => {
cy.get('m-group--sidebar-markers li:nth-child(3)').contains('test group').click();
cy.get('.m-groupSidebarMarkers__list').children().its('length').then((size) => {
// cy.get(`m-group--sidebar-markers li:nth-child(${size - 2})`).click();
cy.contains(groupId).click();
// cleanup
cy.get('minds-groups-settings-button > button').click();
cy.contains('Delete Group').click();
cy.contains('Confirm').click();
// cleanup
cy.get('minds-groups-settings-button > button').click();
cy.get('minds-groups-settings-button ul.minds-dropdown-menu > li:nth-child(8)').contains('Delete Group').click();
cy.get('minds-groups-settings-button m-modal .mdl-button--raised').contains('Confirm').click();
cy.location('pathname').should('eq', '/groups/member');
cy.location('pathname').should('eq', '/groups/member');
})
cy.get('.m-groupSidebarMarkers__list').children().should('have.length', size - 1);
});
});
})
......@@ -5,36 +5,36 @@ context('Login', () => {
})
it('should login', () => {
cy.get('.m-v2-topbar__Container__LoginWrapper > a').click();
cy.get('.m-v2-topbar__Container__LoginWrapper > a').contains('Login').click();
cy.location('pathname').should('eq', '/login');
// it should have a login form
cy.get('.m-login').should('be.visible');
cy.get('.m-login__wrapper').should('be.visible');
cy.get('minds-form-login .m-login-box .mdl-cell:first-child input').type(Cypress.env().username);
cy.get('minds-form-login .m-login-box .mdl-cell:last-child input').type(Cypress.env().password);
cy.get('minds-form-login .m-btn--login').click();
cy.get('minds-form-login .mf-button--alt').click();
cy.location('pathname')
.should('eq', '/newsfeed/subscriptions');
})
it('should fail to login because of incorrect password', () => {
cy.get('.m-v2-topbar__Container__LoginWrapper > a').click();
cy.get('.m-v2-topbar__Container__LoginWrapper > a').contains('Login').click();
cy.location('pathname').should('eq', '/login');
// it should have a login form
cy.get('.m-login').should('be.visible');
cy.get('.m-login__wrapper').should('be.visible');
cy.get('minds-form-login .m-login-box .mdl-cell:first-child input').type(Cypress.env().username);
cy.get('minds-form-login .m-login-box .mdl-cell:last-child input').type(Cypress.env().password + '1');
cy.get('minds-form-login .m-btn--login').click();
cy.get('minds-form-login .mf-button--alt').click();
cy.wait(500);
......
context('Onboarding', () => {
const email = 'test@minds.com';
const password = 'Passw0rd!';
const name = "Tester";
const description = "I am a tester, with a not so lengthy description";
const welcomeText = "Welcome to Minds!";
const usernameField = 'minds-form-register #username';
const emailField = 'minds-form-register #email';
const passwordField = 'minds-form-register #password';
const password2Field = 'minds-form-register #password2';
const nameField = '#display-name';
const descriptionfield = '#description';
const phoneNumberInput = '#phone';
const countryDropdown = 'm-phone-input--country > div';
const ukOption = 'm-phone-input--country > ul > li:nth-child(2)';
const dialcode = '.m-phone-input--dial-code';
const checkbox = 'minds-form-register label:nth-child(2) .mdl-ripple--center';
const submitButton = 'minds-form-register .mdl-card__actions button';
const nextButton = '.m-channelOnboarding__next';
const submitPhoneButton = 'm-channel--onboarding--rewards > div > div > button';
const loadingSpinner = '.mdl-spinner__gap-patch';
const getTopic = (i) => `m-onboarding--topics > div > ul > li:nth-child(${i}) span`;
before(() => {
cy.clearCookies();
cy.visit('/login');
//type values
cy.get(usernameField).focus().type(Math.random().toString(36).replace('0.', ''));
cy.get(emailField).focus().type(email);
cy.get(passwordField).focus().type(password);
cy.get(password2Field).focus().type(password);
cy.get(checkbox).click();
//submit
cy.get(submitButton).click();
//onboarding modal shown
cy.get('m-onboarding--topics > div > h2:nth-child(1)')
.contains(welcomeText);
});
it('should allow a user to run through onboarding modals', () => {
//select topics
cy.get(getTopic(3)).click().should('have.class', 'selected')
cy.get(getTopic(4)).click().should('have.class', 'selected')
cy.get(getTopic(5)).click().should('have.class', 'selected')
//click
cy.get(nextButton).click();
//TODO: Skipped over for now as subscribed channels is not working on staging environment.
cy.get(nextButton).click();
cy.get(nameField).clear().type(name);
cy.get(descriptionfield).type(description);
cy.get(nextButton).click();
//set dialcode
cy.get(countryDropdown).click();
cy.get(ukOption).click();
cy.get(dialcode).contains('+44');
//type number
cy.get(phoneNumberInput).type('7700000000');
//submit and check loading spinner.
cy.get(submitPhoneButton).click();
cy.get(loadingSpinner).should('be.visible');
cy.get(nextButton).click();
});
});
context('Onboarding', () => {
const remindText = 'remind test text';
before(() => {
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
}
});
cy.visit(`/onboarding`);
// create two test groups
});
beforeEach(() => {
cy.preserveCookies();
});
it('should go through the process of onboarding', () => {
// notice should appear
cy.get('h1.m-onboarding__noticeTitle').contains('Welcome to the Minds Community');
cy.get('h2.m-onboarding__noticeTitle').contains(`@${Cypress.env().username}`);
// should redirect to /hashtags
cy.get('.m-onboarding__form button.mf-button').contains("Let's Get Setup").click();
cy.wait(1000);
// should be in the hashtags step
// should have a Profile Setup title
cy.get('.m-onboarding__form > h2').contains('Profile Setup');
// should have a progressbar, with the hashtags step highlighted
cy.get('.m-onboardingProgressbar__item--selected span').contains('1');
cy.get('.m-onboardingProgressbar__item--selected span').contains('Hashtags');
// should have a description
cy.get('.m-onboarding__form .m-onboarding__description').contains('Select some hashtags that are of interest to you.');
// should have a list of selectable hashtags
cy.get('.m-hashtags__list li.m-hashtagsList__item').contains('Art').click();
cy.get('.m-hashtags__list li.m-hashtagsList__item.m-hashtagsList__item--selected').contains('Art');
cy.get('.m-hashtags__list li.m-hashtagsList__item').contains('Journalism').click();
cy.get('.m-hashtags__list li.m-hashtagsList__item.m-hashtagsList__item--selected');
cy.get('.m-hashtags__list li.m-hashtagsList__item').contains('Music').click();
cy.get('.m-hashtags__list li.m-hashtagsList__item.m-hashtagsList__item--selected');
// should have a continue and a skip button
cy.get('button.mf-button--hollow').contains('Skip');
cy.get('button.mf-button--alt').contains('Continue').click();
// should be in the info step
cy.get('.m-onboardingProgressbar__item--selected span').contains('2');
cy.get('.m-onboardingProgressbar__item--selected span').contains('Info');
// should have a Mobile Phone Number input
cy.get('.m-onboarding__controls .m-onboarding__control label').contains('Mobile Phone Number');
// open country dropdown
cy.get('.m-onboarding__controls .m-phone-input--selected-flag').click();
// click on UK
cy.get('.m-phone-input--country-list li span[data-minds=54]').click();
// Uk should be selected
cy.get('.m-phone-input--selected-flag .m-phone-input--dial-code').contains('+54');
// add the number
cy.get('#phone').type('012345678');
// should have a Location input
cy.get('.m-onboarding__controls > .m-onboarding__control label[data-minds=location]').contains('Location');
cy.get('.m-onboarding__controls > .m-onboarding__control input[data-minds=locationInput]').type('London');
// should have Date of Birth inputs
cy.get('.m-onboarding__controls > .m-onboarding__control label[data-minds=dateOfBirth]').contains('Date of Birth');
// open month selection and pick February
cy.get('.m-onboarding__controls > .m-onboarding__control select[data-minds=monthDropdown]').select('February');
// open day selection and pick 2nd
cy.get('.m-onboarding__controls > .m-onboarding__control select[data-minds=dayDropdown]').select('2');
// open year selection and pick 1991
cy.get('.m-onboarding__controls > .m-onboarding__control select[data-minds=yearDropdown]').select('1991');
// should have a continue and a skip button
cy.get('button.mf-button--hollow').contains('Skip');
cy.get('button.mf-button--alt').contains('Continue').click();
// should be in the Groups step
// should have a groups list
// cy.get('.m-groupList__list').should('exist');
// clicking on a group join button should join the group
// cy.get('.m-groupList__list .m-groupList__item:first-child .m-join__subscribe').contains('add').click();
// // button should change to a check, and clicking on it should leave the group
// cy.get('.m-groupList__list .m-groupList__item:first-child .m-join__subscribed').contains('check').click();
// cy.get('.m-groupList__list .m-groupList__item:first-child .m-join__subscribe i').contains('add');
// should have a continue and a skip button
cy.get('button.mf-button--hollow').contains('Skip');
cy.get('button.mf-button--alt').contains('Continue').click();
// should be in the Channels step
// should have a channels list
// cy.get('.m-channelList__list').should('exist');
// // clicking on a group join button should join the group
// cy.get('.m-channelList__list .m-channelList__item:first-child .m-join__subscribe').contains('add').click();
// // button should change to a check, and clicking on it should leave the channel
// cy.get('.m-channelList__list .m-channelList__item:first-child .m-join__subscribed').contains('check').click();
// cy.get('.m-channelList__list .m-channelList__item:first-child .m-join__subscribe i').contains('add');
// should have a continue and a skip button
cy.get('button.mf-button--hollow').contains('Skip');
cy.get('button.mf-button--alt').contains('Finish').click();
// should be in the newsfeed
cy.location('pathname').should('eq', '/newsfeed/subscriptions');
});
});
......@@ -11,15 +11,17 @@ context('Pro Product Page', () => {
cy.preserveCookies();
});
const upgradeButton = 'm-pro--subscription .mf-button';
const upgradeButton = '[data-cy=data-minds-pro-upgrade-button]';
const wirePaymentsComponent = 'm-wire__paymentscreator .m-wire--creator';
it('should show a coming soon button', () => {
cy.visit('/pro');
it('should show an Upgrade to Pro button', () => {
cy.visit('/pro')
.location('pathname')
.should('eq', '/pro');
cy.get(upgradeButton)
.should('be.visible')
.should('contain', 'Coming soon')
.should('contain', 'Upgrade to Pro')
.click();
});
......
......@@ -5,11 +5,6 @@ context('Registration', () => {
const username = generateRandomId();
const password = `${generateRandomId()}0oA!`;
const email = 'test@minds.com';
const noSymbolPass = 'Passw0rd';
const welcomeText = "Welcome to Minds!";
const passwordDontMatch = "Passwords must match.";
const passwordInvalid = " Password must have more than 8 characters. Including uppercase, numbers, special characters (ie. !,#,@), and cannot have spaces. ";
const usernameField = 'minds-form-register #username';
const emailField = 'minds-form-register #email';
......@@ -20,8 +15,8 @@ context('Registration', () => {
beforeEach(() => {
cy.clearCookies();
cy.visit('/login');
cy.location('pathname').should('eq', '/login');
cy.visit('/register');
cy.location('pathname').should('eq', '/register');
cy.server();
cy.route("POST", "**/api/v1/register").as("register");
});
......@@ -39,55 +34,21 @@ context('Registration', () => {
cy.get(usernameField)
.focus()
.type(username);
cy.get(emailField)
.focus()
.type(email);
cy.get(passwordField)
.focus()
.type(password);
cy.wait(500);
cy.get(password2Field)
.focus()
.type(password);
cy.get(checkbox)
.click({force: true});
//submit
cy.get(submitButton)
.click()
.wait('@register').then((xhr) => {
expect(xhr.status).to.equal(200);
});
//onboarding modal shown
cy.contains(welcomeText);
});
it('should display an error if password is invalid', () => {
cy.get(usernameField)
.focus()
.type(generateRandomId());
cy.get(emailField)
.focus()
.type(email);
cy.get(passwordField)
.focus()
.type(noSymbolPass);
cy.wait(500);
cy.get(password2Field)
.focus()
.type(noSymbolPass);
cy.get(checkbox)
.click({force: true});
......@@ -98,37 +59,22 @@ context('Registration', () => {
expect(xhr.status).to.equal(200);
});
cy.scrollTo('top');
cy.contains(passwordInvalid);
cy.wait(500);
cy.location('pathname').should('eq', '/newsfeed/subscriptions');
});
it('should display an error if passwords do not match', () => {
cy.get(usernameField)
.focus()
.type(generateRandomId());
cy.get(emailField)
.focus()
.type(email);
cy.get('minds-form-register #password')
.focus()
.type(password);
cy.wait(500);
cy.get(password2Field)
.focus()
.type(password + '!');
cy.get(checkbox)
.click({force: true});
//submit
cy.get(submitButton).click();
cy.scrollTo('top');
cy.contains(passwordDontMatch);
cy.get('.m-register__error').contains('Passwords must match');
});
})
import 'cypress-file-upload';
/**
* @author Marcelo, Ben and Brian
* @author Marcelo, Ben and Brian
* @create date 2019-08-09 22:54:02
* @modify date 2019-08-09 22:54:02
* @desc Custom commands for access through cy.[cmd]();
*
*
* For more comprehensive examples of custom
* commands please read more here:
* https://on.cypress.io/custom-commands
*
* -- This is a parent command --
* Cypress.Commands.add('login', (email, password) => { ... })
* -- This is a child command --
* Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
* -- This is a dual command --
* Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
* -- This is will overwrite an existing command --
* Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
*/
......@@ -56,7 +56,7 @@ const defaults = {
const loginForm = {
password: 'minds-form-login .m-login-box .mdl-cell:last-child input',
username: 'minds-form-login .m-login-box .mdl-cell:first-child input',
submit: 'minds-form-login .m-btn--login',
submit: '[data-cy=data-minds-login-button]',
}
const poster = {
......@@ -84,7 +84,7 @@ Cypress.Commands.add('login', (canary = false, username, password) => {
cy.get(loginForm.username).focus().type(username);
cy.get(loginForm.password).focus().type(password);
cy.get(loginForm.submit)
.focus()
.click({force: true})
......@@ -104,21 +104,21 @@ Cypress.Commands.add('logout', () => {
/**
* Register a user, be sure to delete the user following this.
*
*
* ! LOG-OUT PRIOR TO CALLING !
*
*
* @param { string } username - The username. Note that the requested username will NOT be freed up upon deletion
* @param { string } password - The users password.
* @returns void
*/
Cypress.Commands.add('newUser', (username = '', password = '') => {
cy.visit('/login')
cy.visit('/register')
.location('pathname')
.should('eq', `/login`);
.should('eq', `/register`);
cy.server();
cy.route("POST", '**/api/v1/register').as('registerPOST');
cy.get(registerForm.username).focus().type(username);
cy.get(registerForm.email).focus().type(defaults.email);
cy.get(registerForm.password).focus().type(password);
......@@ -137,7 +137,7 @@ Cypress.Commands.add('newUser', (username = '', password = '') => {
//onboarding modal shown.
cy.get(onboarding.welcomeTextContainer)
.contains(onboarding.welcomeText);
//skip onboarding.
cy.get(onboarding.nextButton).click()
cy.get(onboarding.nextButton).click()
......@@ -151,14 +151,14 @@ Cypress.Commands.add('preserveCookies', () => {
/**
* Deletes a user. Use carefully on sandbox or you may lose your favorite test account.
*
*
* ! LOG-IN PRIOR TO CALLING !
*
*
* @param { string } username - The username. TODO: when both params provided log the user in too
* @param { string } password - The password.
* @returns void
*/
Cypress.Commands.add('deleteUser', (username, password) => {
Cypress.Commands.add('deleteUser', (username, password) => {
cy.server();
cy.route("POST", '**/api/v2/settings/password/validate').as('validatePost');
cy.route("POST", '**/api/v2/settings/delete').as('deletePOST');
......@@ -170,7 +170,7 @@ Cypress.Commands.add('deleteUser', (username, password) => {
cy.get(settings.deleteAccountButton).click({ force: true });
cy.get('#password').focus().type(password);
cy.get(settings.deleteSubmitButton).click({ force: true })
cy.get(settings.deleteSubmitButton).click({ force: true })
.wait('@validatePost').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("success");
......@@ -193,9 +193,9 @@ Cypress.Commands.add('uploadFile', (selector, fileName, type = '') => {
cy.fixture(fileName).then((content) => {
cy.log("Content", fileName);
cy.get(selector).upload({
fileContent: content,
fileName: fileName,
mimeType: type
fileContent: content,
fileName: fileName,
mimeType: type
});
});
});
......@@ -218,7 +218,7 @@ Cypress.Commands.add('post', (message) => {
/**
* Sets the feature flag cookie.
* @param { Object } flags - JSON object containing flags to turn on
* @param { Object } flags - JSON object containing flags to turn on
* e.g. { dark mode:false, es-feeds: true }
* @returns void
*/
......
This diff is collapsed.
......@@ -20,6 +20,7 @@
"private": true,
"dependencies": {
"@angular/animations": "~8.0.3",
"@angular/cdk": "^8.2.3",
"@angular/common": "~8.0.3",
"@angular/compiler": "~8.0.3",
"@angular/core": "~8.0.3",
......@@ -28,7 +29,7 @@
"@angular/platform-browser": "~8.0.3",
"@angular/platform-browser-dynamic": "~8.0.3",
"@angular/router": "~8.0.3",
"@sentry/browser": "^5.6.2",
"@sentry/browser": "^5.11.1",
"angular-plotly.js": "^1.3.2",
"bn.js": "^4.11.8",
"braintree-web": "3.41.0",
......@@ -39,10 +40,11 @@
"ethjs-provider-signer": "^0.1.4",
"ethjs-signer": "0.1.1",
"global": "^4.3.2",
"material-datetime-picker": "git+https://github.com/Minds/material-datetime-picker.git",
"material-design-icons": "~3.0.1",
"material-design-lite": "~1.3.0",
"medium-editor": "^5.23.2",
"moment": "^2.24.0",
"ng-pick-datetime": "^7.0.0",
"ngx-drag-drop": "^2.0.0",
"ngx-plyr": "^3.0.1",
"plotly.js": "^1.47.4",
......
......@@ -34,13 +34,13 @@
[class.is-pro-domain]="isProDomain"
>
<m-emailConfirmation></m-emailConfirmation>
<m-announcement [id]="'blockchain:sale'" *ngIf="false">
<m-announcement [id]="'festival:sale'" *mIfFeature="'radiocity'">
<span
class="m-blockchain--wallet-address-notice--action"
routerLink="/tokens"
i18n="@@BLOCKCHAIN__SALE__NOTICE"
i18n="@@MINDS_FESTIVAL_TICKET_SALE"
>
The MINDS token is now live. Learn more here.
BREAKING: Tickets on sale for "MINDS: FESTIVAL OF IDEAS" @ Radio City on
6/13/2020. HELP US SELL OUT FAST!
</span>
</m-announcement>
<m-blockchain--wallet-address-notice></m-blockchain--wallet-address-notice>
......@@ -58,7 +58,10 @@
*ngIf="session.isLoggedIn() && !isProDomain"
></m-juryDutySession__summons>
<m-modal-signup-on-scroll *ngIf="!isProDomain"></m-modal-signup-on-scroll>
<m-modal-signup
*ngIf="!isProDomain && !session.getLoggedInUser()"
[open]="false"
></m-modal-signup>
<m-channel--onboarding *ngIf="showOnboarding"></m-channel--onboarding>
......
......@@ -113,7 +113,10 @@ export class Minds {
this.minds.user.language,
this.minds.language
);
window.location.reload(true);
setTimeout(() => {
window.location.reload(true);
});
}
}
});
......
......@@ -52,7 +52,6 @@ import { BanModule } from './modules/ban/ban.module';
import { BlogModule } from './modules/blogs/blog.module';
import { SearchModule } from './modules/search/search.module';
import { MessengerModule } from './modules/messenger/messenger.module';
import { HomepageModule } from './modules/homepage/homepage.module';
import { NewsfeedModule } from './modules/newsfeed/newsfeed.module';
import { MediaModule } from './modules/media/media.module';
import { AuthModule } from './modules/auth/auth.module';
......@@ -73,6 +72,8 @@ import { ChannelContainerModule } from './modules/channel-container/channel-cont
import { UpgradesModule } from './modules/upgrades/upgrades.module';
import * as Sentry from '@sentry/browser';
import { HomepageModule } from './modules/homepage/homepage.module';
import { OnboardingV2Module } from './modules/onboarding-v2/onboarding.module';
Sentry.init({
dsn: 'https://3f786f8407e042db9053434a3ab527a2@sentry.io/1538008', // TODO: do not hardcard
......@@ -127,6 +128,7 @@ export class SentryErrorHandler implements ErrorHandler {
PaymentsModule,
MindsFormsModule,
OnboardingModule,
OnboardingV2Module,
NotificationModule,
GroupsModule,
BlogModule,
......
......@@ -121,12 +121,18 @@ import { PageLayoutComponent } from './components/page-layout/page-layout.compon
import { DashboardLayoutComponent } from './components/dashboard-layout/dashboard-layout.component';
import { ShadowboxLayoutComponent } from './components/shadowbox-layout/shadowbox-layout.component';
import { ShadowboxHeaderComponent } from './components/shadowbox-header/shadowbox-header.component';
import { OwlDateTimeModule, OwlNativeDateTimeModule } from 'ng-pick-datetime';
import { DropdownSelectorComponent } from './components/dropdown-selector/dropdown-selector.component';
import { ShadowboxSubmitButtonComponent } from './components/shadowbox-submit-button/shadowbox-submit-button.component';
import { FormDescriptorComponent } from './components/form-descriptor/form-descriptor.component';
import { FormToastComponent } from './components/form-toast/form-toast.component';
import { SsoService } from './services/sso.service';
import { PagesService } from './services/pages.service';
import { V2TopbarService } from './layout/v2-topbar/v2-topbar.service';
import { DateDropdownsComponent } from './components/date-dropdowns/date-dropdowns.component';
import { SidebarMarkersService } from './layout/sidebar/markers.service';
import { EmailConfirmationComponent } from './components/email-confirmation/email-confirmation.component';
import { HorizontalFeedService } from './services/horizontal-feed.service';
PlotlyModule.plotlyjs = PlotlyJS;
......@@ -145,6 +151,8 @@ const routes: Routes = [
FormsModule,
ReactiveFormsModule,
PlotlyModule,
OwlDateTimeModule,
OwlNativeDateTimeModule,
RouterModule.forChild(routes),
],
declarations: [
......@@ -251,6 +259,7 @@ const routes: Routes = [
FormToastComponent,
ShadowboxSubmitButtonComponent,
EmailConfirmationComponent,
DateDropdownsComponent,
],
exports: [
MINDS_PIPES,
......@@ -351,10 +360,12 @@ const routes: Routes = [
FormToastComponent,
ShadowboxSubmitButtonComponent,
EmailConfirmationComponent,
DateDropdownsComponent,
],
providers: [
SiteService,
SsoService,
PagesService,
{
provide: AttachmentService,
useFactory: AttachmentService._,
......@@ -417,6 +428,15 @@ const routes: Routes = [
useFactory: router => new RouterHistoryService(router),
deps: [Router],
},
{
provide: V2TopbarService,
useFactory: V2TopbarService._,
},
{
provide: SidebarMarkersService,
useFactory: SidebarMarkersService._,
},
HorizontalFeedService,
],
entryComponents: [
NotificationsToasterComponent,
......
import { Component, EventEmitter, Input } from '@angular/core';
import { Component, EventEmitter, Input, OnInit } from '@angular/core';
import { Storage } from '../../../services/storage';
import { Client } from '../../../services/api';
......@@ -20,7 +20,7 @@ import { Client } from '../../../services/api';
</div>
`,
})
export class AnnouncementComponent {
export class AnnouncementComponent implements OnInit {
minds: Minds = window.Minds;
hidden: boolean = false;
@Input() id: string = 'default';
......
......@@ -130,6 +130,10 @@ export class MindsAvatar {
* @returns true if the object guid matches the currently logged in user guid
*/
isOwnerAvatar(): boolean {
return this.minds.user && this.object.guid === this.minds.user.guid;
return (
this.minds.user &&
this.object &&
this.object.guid === this.minds.user.guid
);
}
}
m-date__dropdowns {
display: flex;
justify-content: space-between;
select {
display: inline-block;
background-color: #fff;
box-sizing: border-box;
margin: 0 10px 0 0;
padding: 8px 10px;
height: 36px;
min-width: 80px;
max-width: 90px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
font-size: 16px;
line-height: 21px;
border-radius: 2px;
@include m-theme() {
color: themed($m-grey-800);
background-color: themed($m-white);
border: 1px solid #e2e2e2;
}
// month
&:nth-child(1) {
min-width: 120px;
}
// day
&:nth-child(2) {
min-width: 59px;
}
// year
&:nth-child(3) {
min-width: 77px;
}
}
}
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
@Component({
selector: 'm-date__dropdowns',
template: `
<select
data-minds="monthDropdown"
[ngModel]="selectedMonth"
(ngModelChange)="selectMonth($event)"
>
<option *ngFor="let month of monthNames">{{ month }}</option>
</select>
<select
data-minds="dayDropdown"
[ngModel]="selectedDay"
(ngModelChange)="selectDay($event)"
>
<option *ngFor="let day of days">{{ day }}</option>
</select>
<select
data-minds="yearDropdown"
[ngModel]="selectedYear"
(ngModelChange)="selectYear($event)"
>
<option *ngFor="let year of years">{{ year }}</option>
</select>
`,
})
export class DateDropdownsComponent implements OnInit {
@Output() selectedDateChange: EventEmitter<string> = new EventEmitter<
string
>();
monthNames = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
days = [1];
years = [];
selectedMonth = 'January';
selectedDay = '1';
selectedYear = new Date().getFullYear();
constructor() {}
ngOnInit() {
this.years = this.range(100, this.selectedYear, false);
this.selectedYear = this.years[0];
this.selectMonth('January');
}
selectMonth(month: string) {
this.selectedMonth = month;
this.populateDays(
this.getDaysInMonth(this.getMonthNumber(month), this.selectedYear)
);
this.selectedDateChange.emit(this.buildDate());
}
selectDay(day: string) {
this.selectedDay = day;
this.selectedDateChange.emit(this.buildDate());
}
selectYear(year) {
this.selectedYear = year;
this.populateDays(
this.getDaysInMonth(this.getMonthNumber(this.selectedMonth), year)
);
this.selectedDateChange.emit(this.buildDate());
}
buildDate() {
let date: string = '';
if (this.selectedMonth !== '') {
if (this.selectedYear) {
date = `${this.pad(this.selectedYear, 4)}-`;
}
const monthIndex = this.monthNames.findIndex(
item => item === this.selectedMonth
);
date += `${this.pad(monthIndex + 1, 2)}`;
if (this.selectedDay) {
date += `-${this.pad(this.selectedDay, 2)}`;
}
}
return date;
}
private populateDays(maxDays: number) {
this.days = this.range(maxDays, 1);
}
private getMonthNumber(month: string): number {
return this.monthNames.indexOf(month);
}
private getDaysInMonth(month, year): number {
// let date = new Date(Date.UTC(year, month, 1));
const date = new Date(year, month, 1);
let day = 0;
while (date.getMonth() === month) {
day = date.getDate();
date.setDate(date.getDate() + 1);
}
return day;
}
private range(size, startAt = 0, grow = true): Array<number> {
return Array.from(Array(size).keys()).map(i => {
if (grow) {
return i + startAt;
} else {
return startAt - i;
}
});
}
private pad(val: any, pad: number = 0) {
if (!pad) {
return val;
}
return (Array(pad + 1).join('0') + val).slice(-pad);
}
}
m-date-selector {
.m-date-selector--label {
text-transform: uppercase;
letter-spacing: 2.5px;
align-self: center;
font-size: 12px;
@include m-theme() {
color: themed($m-grey-600);
}
.m-dateSelector__label {
text-transform: uppercase;
letter-spacing: 2.5px;
align-self: center;
font-size: 12px;
@include m-theme() {
color: themed($m-grey-600);
}
}
.m-dateSelector__input {
font-size: 12px;
text-transform: uppercase;
text-align: center;
padding: 8px 0;
@media screen and (max-width: $min-tablet) {
margin: 8px 0;
}
@include m-theme() {
color: themed($m-grey-600);
background-color: themed($m-white);
}
}
.m-date-selector--input {
display: flex;
align-items: center;
.cdk-overlay-container {
z-index: 10000 !important;
.owl-dt-container {
@include m-theme() {
color: themed($m-grey-600);
background-color: themed($m-white);
//border: 1px solid themed($m-grey-100);
}
background: themed($m-white);
input {
font-size: 12px;
text-transform: uppercase;
background-color: transparent;
padding: 8px 0;
border: none;
text-align: center;
width: auto;
height: auto;
align-self: center;
@include m-theme() {
color: rgba(themed($m-black), 0.54);
* {
color: themed($m-grey-600);
}
}
i {
vertical-align: middle;
cursor: pointer;
margin-right: 8px;
.owl-dt-calendar-cell-selected {
background-color: themed($m-blue);
color: themed($m-white);
}
}
}
}
.m-dateSelector__input--hidden {
visibility: hidden;
position: absolute;
right: 0;
bottom: 0;
}
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { DatePipe } from '@angular/common';
/**
* Date and time picker / selector.
* @author Ben Hayward
*/
@Component({
moduleId: module.id,
selector: 'm-date-selector',
template: `
<label class="m-date-selector--label" *ngIf="label">{{ label }}</label>
<div
class="m-date-selector--input"
mdl-datetime-picker
[date]="date"
(dateChange)="onDateChange($event)"
<label class="m-dateSelector__label" *ngIf="label">{{ label }} </label>
<input
class="m-dateSelector__input"
[ngClass]="{ 'm-dateSelector__input--hidden': hideInput }"
[owlDateTimeTrigger]="dt"
[owlDateTime]="dt"
[min]="min"
[max]="max"
[ngModel]="date"
(ngModelChange)="onDateChange($event)"
/>
<owl-date-time [pickerType]="calendarType" #dt></owl-date-time>
<m-tooltip
*ngIf="tooltipIcon"
icon="{{ tooltipIcon }}"
[owlDateTimeTrigger]="dt"
i18n-label="{{ i18n }}"
>
<input
type="text"
placeholder="Select a date"
i18n-placeholder="@@COMMON__DATE_SELECTOR__PLACEHOLDER"
[ngModel]="date | date: dateFormat"
(ngModelChange)="onDateChange($event)"
/>
<i class="material-icons">keyboard_arrow_down</i>
</div>
{{ tooltipText }}
</m-tooltip>
`,
providers: [DatePipe],
})
export class DateSelectorComponent {
@Input() label: string;
@Input() date: string;
@Output() dateChange: EventEmitter<any> = new EventEmitter<any>();
@Input() dateFormat: string = 'short';
@Input() dateFormat: string = 'short'; // legacy. TODO: implement localization.
@Input() label: string; // label for input.
onDateChange(newDate) {
@Input() hideInput = false; // text input showing the date.
@Input() calendarType = 'calendar'; // timer/calendar/both.
@Input() i18n?: string; // i18n string to accompany tooltip text.
@Input() tooltipIcon?: string; // tooltip icon.
@Input() tooltipText?: string; // tooltip text.
protected _date: any;
@Input('date') // parse input into Date object.
set date(value: number) {
// If ms not included in timestamp, multiply..
if (value && value.toString().length <= 10) {
value = value * 1000;
}
this._date = new Date(value ? value : Date.now());
}
get date() {
return this._date;
}
protected _min: Date;
@Input('min') // parse input into Date object.
set min(value) {
this._min = new Date(value);
}
get min(): any {
return this._min;
}
protected _max: Date;
@Input('max') // parse input into Date object.
set max(value) {
this._max = new Date(value);
}
get max(): any {
return this._max;
}
/**
* Called when date changes.
* @param newDate - the new date.
*/
public onDateChange(newDate: number): void {
this.dateChange.emit(newDate);
}
}