...
 
Commits (5)
......@@ -12,9 +12,9 @@ function usage()
local help="Usage: e2e.sh [OPTIONS]
Intended to serve as an interaction wrapper around Cypress.
Intended to serve as an interaction wrapper around Cypress. Ensure that you run from within the project.
Example: ./e2e.sh -u nemofin -p password123 -v true -url http://www.minds.com/
Example: ./e2e.sh -u nemofin -p password123 -v true -h http://www.minds.com/
Options (* indicates it is required):"
local help_options="
......@@ -117,7 +117,7 @@ done
init_args $@
# cd to project root.
# while [[ $PWD != '/' && ${PWD##*/} != 'front' ]]; do cd ..; done
while [[ $PWD != '/' && ${PWD##*/} != 'front' ]]; do cd ..; done
#run cypress with args.
echo $(npm bin)/cypress open --config baseUrl=$url,video=$_video --env username=$username,password=$password,pro_username=$pro_username,pro_password=$pro_password$env $POSITIONAL
......
......@@ -2,24 +2,29 @@
context('Blogs', () => {
before(() => {
cy.clearCookies();
cy.getCookie('minds_sess').then(sessionCookie => {
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
}
});
});
beforeEach(() => {
beforeEach(()=> {
cy.preserveCookies();
cy.server();
cy.route('POST', '**/api/v1/blog/new').as('postBlog');
cy.route('POST', '**/api/v1/media**').as('postMedia');
cy.route('GET', '**/api/v1/blog/**').as('getBlog');
cy.route('DELETE', '**/api/v1/blog/**').as('deleteBlog');
cy.visit('/newsfeed/global/top')
.location('pathname')
.should('eq', '/newsfeed/global/top');
});
const uploadAvatar = () => {
cy.visit(`/${Cypress.env().username}`, { timeout: 30000 });
cy.visit(`/${Cypress.env().username}`);
cy.get('.m-channel--name .minds-button-edit button:first-child').click();
cy.uploadFile(
'.minds-avatar input[type=file]',
......@@ -30,7 +35,7 @@ context('Blogs', () => {
};
const createBlogPost = (title, body, nsfw = false, schedule = false) => {
cy.visit('/blog/edit/new', { timeout: 30000 });
cy.visit('/blog/edit/new');
cy.uploadFile(
'.minds-banner input[type=file]',
......@@ -41,12 +46,17 @@ context('Blogs', () => {
cy.get('minds-textarea .m-editor').type(title);
cy.get('m-inline-editor .medium-editor-element').type(body);
// click on plus button
cy.get('.medium-editor-element > .medium-insert-buttons > button.medium-insert-buttons-show').click();
// click on camera
cy.get('ul.medium-insert-buttons-addons > li > button.medium-insert-action:first-child').contains('photo_camera').click();
// // click on plus button
// cy.get('.medium-editor-element > .medium-insert-buttons > button.medium-insert-buttons-show').click();
// // click on camera
// cy.get('ul.medium-insert-buttons-addons > li > button.medium-insert-action:first-child').contains('photo_camera').click();
// upload the image
cy.uploadFile('.medium-media-file-input', '../fixtures/international-space-station-1776401_1920.jpg', 'image/jpg');
cy.uploadFile('.medium-media-file-input', '../fixtures/international-space-station-1776401_1920.jpg', 'image/jpg')
.wait('@postMedia').then(xhr => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
});
// open license dropdown & select first license
cy.get('.m-license-info select').select('All rights reserved');
......@@ -108,23 +118,20 @@ context('Blogs', () => {
});
}
cy.get('.m-button--submit', { timeout: 5000 }).click({ force: true }); // TODO: Investigate why disabled flag is being detected
cy.wait('@postBlog').then(xhr => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
cy.wait('@getBlog').then(xhr => {
cy.get('.m-button--submit')
.click({ force: true }) // TODO: Investigate why disabled flag is being detected
.wait('@postBlog').then(xhr => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
})
.wait('@getBlog').then(xhr => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
expect(xhr.response.body).to.have.property('blog');
});
});
cy.location('pathname', { timeout: 30000 }).should(
'contains',
`/${Cypress.env().username}/blog`
);
cy.location('pathname')
.should('contains', `/${Cypress.env().username}/blog`);
cy.get('.m-blog--title').contains(title);
cy.get('.minds-blog-body p').contains(body);
......@@ -191,14 +198,8 @@ context('Blogs', () => {
cy.get('.minds-blog-body p').contains(body);
};
it('should be able to create a new scheduled blog', () => {
uploadAvatar();
createBlogPost('Title', 'Content', true, true);
deleteBlogPost();
});
it('should not be able to create a new blog if no title or banner are specified', () => {
cy.visit('/blog/edit/new', { timeout: 30000 });
cy.visit('/blog/edit/new');
cy.get('.m-button--submit').click();
cy.get('.m-blog--edit--error').contains('Error: You must provide a title');
cy.get('minds-textarea .m-editor').type('Title');
......@@ -215,7 +216,21 @@ context('Blogs', () => {
deleteBlogPost();
});
it('should create an activity for the blog post', () => {
/**
* Skipping until the scheduling options are visible on sandboxes
* currently they are not, missing setting perhaps?
*/
it.skip('should be able to create a new scheduled blog', () => {
uploadAvatar();
createBlogPost('Title', 'Content', true, true);
deleteBlogPost();
});
/**
* Skipping until sandbox behaves consistently as currently when posting,
* on the sandbox it does not update the newsfeed and channel straight away as it does on prod.
*/
it.skip('should create an activity for the blog post', () => {
const identifier = Math.floor(Math.random() * 100);
const title = 'Test Post for Activity ' + identifier;
const body = 'Some content here ' + identifier;
......@@ -244,7 +259,11 @@ context('Blogs', () => {
);
});
it('should update the activity when blog is updated', () => {
/**
* Skipping until sandbox behaves consistently as currently when posting,
* on the sandbox it does not update the newsfeed and channel straight away as it does on prod.
*/
it.skip('should update the activity when blog is updated', () => {
const identifier = Math.floor(Math.random() * 100);
const title = 'Test Post for Activity ' + identifier;
const body = 'Some content here ' + identifier;
......
......@@ -14,13 +14,13 @@ context('Boost Console', () => {
return cy.login(true);
}
});
newBoost(postContent, 100);
newBoost(postContent, 500);
});
beforeEach(() => {
cy.preserveCookies();
cy.server();
cy.route("POST", '**/api/v2/boost/**').as('boostPost');
cy.preserveCookies();
cy.visit('/boost/console/newsfeed/history');
});
......
context('Boost Creation', () => {
const duplicateError = "There's already an ongoing boost for this entity";
const postContent = "Testing, please reject..." + Math.random().toString(36).substring(8);
const nonParticipationError = 'Boost target should participate in the Rewards program.'
/**
* @author Ben Hayward
* @desc E2E testing for Minds Boost Console pages.
*/
import generateRandomId from '../../support/utilities';
beforeEach(() => {
cy.server()
.route("GET", '**/api/v2/boost/prepare/**').as('prepare')
.route("POST", '**/api/v2/boost/activity/**').as('activity')
.route("GET", '**/api/v2/blockchain/wallet/balance*').as('balance')
.route("GET", '**/api/v2/search/suggest/**').as('suggest');
context('Boost Console', () => {
const postContent = "Test boost, please reject..." + generateRandomId();
before(() => {
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
}
});
cy.visit(`/${Cypress.env().username}/`)
.location('pathname')
.should('eq', `/${Cypress.env().username}/`);
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
}
});
newBoost(postContent, 500);
});
// Revoke all boosts visible on the screen -
// If adding tests make sure it is cleaned up by this.
after(() => {
beforeEach(() => {
cy.server();
cy.route("POST", '**/api/v2/boost/**').as('boostPost');
cy.preserveCookies();
cy.visit('/boost/console/newsfeed/history');
cy.contains('Revoke')
.click({multiple: true});
});
before(() => {
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
}
});
cy.post(postContent);
after(() => {
cy.clearCookies();
});
it('should redirect a user to buy tokens when clicked', () => {
openModal(postContent);
it('should show a new boost in the console', () => {
cy.get('m-boost-console-card:nth-child(1) div.m-boost-card--manager-item.m-boost-card--state')
.should('not.contain', 'revoked');
cy.get('m-boost-console-card:nth-child(1) .m-mature-message span')
.contains(postContent);
});
cy.get('m-boost--creator-payment-methods li h5 span')
.contains('Buy Tokens')
it('should allow a revoke a boost', () => {
cy.get('m-boost-console-card:nth-child(1) div.m-boost-card--manager-item.m-boost-card--state')
.should('not.contain', 'revoked');
cy.get('m-boost-console-card:nth-child(1) .m-boost-card--manager-item--buttons > button')
.click();
cy.location('pathname', { timeout: 30000 })
.should('eq', `/token`);
cy.get('m-boost-console-card:nth-child(1) div.m-boost-card--manager-item.m-boost-card--state')
.contains('revoked');
});
it('should allow a user to make an offchain boost for 500 tokens', () => {
openModal(postContent);
cy.get('.m-boost--creator-section-amount input')
.type(500);
cy.get('m-overlay-modal > div.m-overlay-modal > m-boost--creator button')
it('should load show the user content for newsfeed boosts', () => {
cy.route("GET", "**/feeds/container/*/activities**").as("activities");
cy.contains('Create a Boost')
.click()
.wait('@prepare').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("success");
}).wait('@activity').then((xhr) => {
.location('pathname')
.should('eq', `/boost/console/newsfeed/create`)
.wait('@activities').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("success");
});
cy.get('.m-overlay-modal')
.should('not.be.visible')
});
it('should error if the boost is a duplicate', () => {
openModal(postContent);
cy.get('.m-boost--creator-section-amount input')
.type(500);
})
cy.get('m-overlay-modal > div.m-overlay-modal > m-boost--creator button')
.click()
.wait('@prepare').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("success");
}).wait('@activity').then((xhr) => {
it('should load show the user content for sidebar boosts', () => {
cy.route("GET", "**/api/v2/feeds/container/*/all**").as("all");
cy.visit('/boost/console/content/create')
.location('pathname')
.should('eq', `/boost/console/content/create`)
.wait('@all').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("error");
});
cy.get('[data-cy=data-minds-boost-creation-error]')
.contains(duplicateError);
});
})
it('should display an error if boost offer receiver has not signed up for rewards', () => {
openModal(postContent);
cy.get('h4')
.contains('Offers')
.click();
cy.get('m-boost--creator-p2p-search .m-boost--creator-wide-input input')
.type("minds").wait('@suggest').then((xhr) => {
it('should load show the user content for offers', () => {
cy.route("GET", "**/api/v2/feeds/container/*/activities**").as("all");
cy.visit('/boost/console/offers/create')
.location('pathname')
.should('eq', `/boost/console/offers/create`)
.wait('@all').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("success");
});
cy.get('.m-boost--creator-autocomplete--results .m-boost--creator-autocomplete--result-content')
})
function newBoost(text, views) {
cy.server();
cy.route("POST", '**/api/v2/boost/**').as('boostPost');
cy.visit('/newsfeed/subscribed');
cy.post(text);
cy.get('#boost-actions')
.first()
.click({force: true});
.click();
cy.get('[data-cy=data-minds-boost-creation-error]')
.contains(nonParticipationError);
});
cy.get('.m-boost--creator-section-amount input')
.type(views);
/*
* Finds the post containing the post content,
* then gets the parent entity before m-newsfeed__entity.
* Following that, we get all of the children, and look
* for the one that contains Boost, and clicks.
*/
function openModal(postText) {
cy.contains(postText)
.parentsUntil('m-newsfeed__entity')
.children()
.contains('Boost')
.click()
.wait('@balance').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("success");
});
cy.get('m-overlay-modal > div.m-overlay-modal > m-boost--creator button')
.click();
cy.wait('@boostPost').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("success");
});
cy.get('.m-overlay-modal')
.should('not.be.visible')
}
})
context('Channel image upload', () => {
/**
* Skipping until sandbox behaves consistently as currently when posting,
* on the sandboxes it does not show your latest image after you have posted it.
* The below code should be functioning correctly once this is resolved.
*/
context.skip('Channel image upload', () => {
before(() => {
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
return cy.login(true);
}
});
});
cy.visit('/newsfeed/subscriptions')
.location('pathname')
.should('eq', '/newsfeed/subscriptions');
});
beforeEach(()=> {
cy.preserveCookies();
cy.server();
......
// skipped until feat release
context('Channel', () => {
context.skip('Channel', () => {
before(() => {
cy.overrideFeatureFlag({
'permissions': true,
});
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
......
context('Comment Permissions', () => {
// skipped until feat release
context.skip('Comment Permissions', () => {
const postMenu = 'minds-activity:first > div > m-post-menu';
const deletePostOption = "m-post-menu > ul > li:visible:contains('Delete')";
......
......@@ -6,7 +6,9 @@ context('Discovery', () => {
return cy.login(true);
}
});
cy.visit('/newsfeed/global/top');
cy.visit('/newsfeed/global/top')
.location('pathname')
.should('eq', '/newsfeed/global/top');
});
beforeEach(()=> {
......
......@@ -14,6 +14,10 @@ context('Newsfeed', () => {
cy.route('POST', '**/api/v1/media').as('mediaPOST');
cy.route('POST', '**/api/v1/newsfeed/**').as('newsfeedEDIT');
cy.route('POST', '**/api/v1/media/**').as('mediaEDIT');
cy.visit('/newsfeed/subscriptions')
.location('pathname')
.should('eq', '/newsfeed/subscriptions');
});
const deleteActivityFromNewsfeed = () => {
......@@ -93,63 +97,6 @@ context('Newsfeed', () => {
cy.location('pathname', { timeout: 20000 }).should('contains', 'media');
};
it('editing media post propagates to activity', () => {
const identifier = Math.floor(Math.random() * 100);
const content = 'This is a post with an image ' + identifier;
newActivityContent(content);
attachImageToActivity();
postActivityAndAwaitResponse(200);
cy.get('.minds-list > minds-activity:first .message').contains(content);
navigateToMediaPageFromNewsfeed();
cy.get('.m-media-content--heading', { timeout: 10000 }).contains(content);
cy.get('.minds-button-edit').click();
const newContent = content + ' changed';
cy.get('minds-textarea .m-editor')
.clear()
.type(newContent);
cy.get('.m-button--submit').click();
cy.wait('@mediaEDIT').then(xhr => {
expect(xhr.status).to.equal(200);
});
navigateToNewsfeed();
cy.get('.minds-list > minds-activity:first .message').contains(newContent);
deleteActivityFromNewsfeed();
});
it('editing a media activity propagates to media post', () => {
const identifier = Math.floor(Math.random() * 100);
const content = 'This is a post with an image ' + identifier;
newActivityContent(content);
attachImageToActivity();
postActivityAndAwaitResponse(200);
cy.get('.minds-list > minds-activity:first .message').contains(content);
cy.get('.minds-list > minds-activity:first .item-image img').should(
'be.visible'
);
const newContent = content + ' changed';
editActivityContent(newContent);
cy.get('.minds-list > minds-activity:first .message').contains(content);
navigateToMediaPageFromNewsfeed();
cy.get('.m-media-content--heading', { timeout: 10000 }).contains(newContent);
navigateToNewsfeed();
deleteActivityFromNewsfeed();
});
it('should post an activity picking hashtags from the dropdown', () => {
newActivityContent('This is a post');
......@@ -199,6 +146,9 @@ context('Newsfeed', () => {
deleteActivityFromNewsfeed();
});
/**
* Commenting out until scheduling is enabled properly on sandboxes
*/
it('should be able to post an activity picking a scheduled date and the edit it', () => {
cy.get('minds-newsfeed-poster').then((poster) => {
if (poster.find('.m-poster-date-selector__input').length > 0) {
......@@ -494,4 +444,71 @@ context('Newsfeed', () => {
cy.location('pathname').should('eq', '/groups/create');
});
/**
* Skipping until sandbox behaves consistently as currently when posting,
* on the sandbox it does not update the newsfeed and channel straight away as it does on prod.
*/
it.skip('editing media post propagates to activity', () => {
const identifier = Math.floor(Math.random() * 100);
const content = 'This is a post with an image ' + identifier;
newActivityContent(content);
attachImageToActivity();
postActivityAndAwaitResponse(200);
cy.get('.minds-list > minds-activity:first .message').contains(content);
navigateToMediaPageFromNewsfeed();
cy.get('.m-media-content--heading', { timeout: 10000 }).contains(content);
cy.get('.minds-button-edit').click();
const newContent = content + ' changed';
cy.get('minds-textarea .m-editor')
.clear()
.type(newContent);
cy.get('.m-button--submit').click();
cy.wait('@mediaEDIT').then(xhr => {
expect(xhr.status).to.equal(200);
});
navigateToNewsfeed();
cy.get('.minds-list > minds-activity:first .message').contains(newContent);
deleteActivityFromNewsfeed();
});
/**
* Skipping until sandbox behaves consistently as currently when posting,
* on the sandbox it does not update the newsfeed and channel straight away as it does on prod.
*/
it.skip('editing a media activity propagates to media post', () => {
const identifier = Math.floor(Math.random() * 100);
const content = 'This is a post with an image ' + identifier;
newActivityContent(content);
attachImageToActivity();
postActivityAndAwaitResponse(200);
cy.contains(content);
cy.get('.minds-list > minds-activity:first .item-image img').should(
'be.visible'
);
const newContent = content + ' changed';
editActivityContent(newContent);
cy.contains(content);
navigateToMediaPageFromNewsfeed();
cy.get('.m-media-content--heading', { timeout: 10000 }).contains(newContent);
navigateToNewsfeed();
deleteActivityFromNewsfeed();
});
});
......@@ -19,6 +19,7 @@ context('Registration', () => {
const submitButton = 'minds-form-register .mdl-card__actions button';
beforeEach(() => {
cy.clearCookies();
cy.visit('/login');
cy.location('pathname').should('eq', '/login');
cy.server();
......
......@@ -20,7 +20,7 @@ context('Topbar', () => {
.contains('View Channel')
.click();
cy.location('pathname').should('eq', `/${Cypress.env().username}`);
cy.location('pathname').should('eq', `/${Cypress.env().username}/`);
});
it('clicking on the dropdown on the right should allow to go to settings', () => {
......
......@@ -72,9 +72,10 @@ const poster = {
* @returns void
*/
Cypress.Commands.add('login', (canary = false, username, password) => {
cy.clearCookies();
cy.setCookie('staging', "1"); // Run in staging mode. Note: does not impact review sites
username = username ? username : Cypress.env().username;
password = password ? password : Cypress.env().password;
cy.setCookie('staging', "1"); // Run in stagin mode. Note: does not impact review sites
cy.visit('/login');
......@@ -221,10 +222,10 @@ Cypress.Commands.add('post', (message) => {
* e.g. { dark mode:false, es-feeds: true }
* @returns void
*/
Cypress.Commands.add('overrideFeatureFlag', (flags) => {
const base64 = Buffer.from(JSON.stringify(flags)).toString("base64");
cy.setCookie('staging-features', base64);
});
// Cypress.Commands.add('overrideFeatureFlag', (flags) => {
// const base64 = Buffer.from(JSON.stringify(flags)).toString("base64");
// cy.setCookie('staging-features', base64);
// });
/**
* Converts base64 to blob format
......
import { Injectable, isDevMode } from '@angular/core';
import { Session } from './session';
import { Router } from '@angular/router';
import { Cookie } from '../services/cookie';
import { includes } from 'lodash';
@Injectable()
export class FeaturesService {
private overrides = [];
protected _features: any;
protected _warnedCache: { [key: string]: number } = {};
private cookie: Cookie = new Cookie();
constructor(private session: Session, private router: Router) {
this._features = window.Minds.features || {};
......@@ -23,16 +20,6 @@ export class FeaturesService {
// Inverted check. Useful for *mIfFeature
return !this.has(feature.substring(1));
}
if (this.cookie.get('staging-features')) {
try {
this.overrides = JSON.parse(atob(this.cookie.get('staging-features')));
} catch (e) {
// Do nothing
}
}
if (feature in this.overrides) {
return this.overrides[feature];
}
if (typeof this._features[feature] === 'undefined') {
if (isDevMode() && !this._hasWarned(feature)) {
......@@ -82,7 +69,7 @@ export class FeaturesService {
return this._warnedCache[feature] + 5000 < Date.now();
}
static _(session: Session, router: Router, cookie: Cookie) {
static _(session: Session, router: Router) {
return new FeaturesService(session, router);
}
}