...
 
Commits (19)
......@@ -9,7 +9,7 @@ stages:
- prepare
- review
- deploy:staging
- test:e2e
- qa
- deploy:canary
- deploy:production
- cleanup
......@@ -34,9 +34,13 @@ lint:
- prettier --check "src/**/*.scss"
- prettier --check "src/**/*.html"
e2e:chrome:
############
# QA Stage #
############
qa:e2e:
image: cypress/browsers:chrome67
stage: test:e2e
stage: qa
variables:
CYPRESS_INSTALL_BINARY: 3.4.1
script:
......@@ -44,12 +48,14 @@ e2e:chrome:
- >
if [ "$CI_BUILD_REF_NAME" == "master" ]; then
export E2E_DOMAIN=https://www.minds.com
export PRODUCTION=true
else
export E2E_DOMAIN=https://$CI_BUILD_REF_SLUG.$KUBE_INGRESS_BASE_DOMAIN
export PRODUCTION=false
fi
- export CYPRESS_baseUrl=$E2E_DOMAIN
- echo "E2E tests for $CI_BUILD_REF_NAME running against $E2E_DOMAIN with user $CYPRESS_username"
- $(npm bin)/cypress run --browser chrome --record --key $CYPRESS_RECORD_ID --config CYPRESS_baseUrl=$E2E_DOMAIN
- $(npm bin)/cypress run --browser chrome --record --key $CYPRESS_RECORD_ID --config CYPRESS_baseUrl=$E2E_DOMAIN,production=$PRODUCTION
artifacts:
when: always
paths:
......@@ -61,6 +67,17 @@ e2e:chrome:
- cache/Cypress
allow_failure: true #manual inspection in case of timeouts
qa:manual:
stage: qa
script:
- echo "Manually approved"
when: manual
only:
refs:
- master
- test/gitlab-ci
allow_failure: false
###############
# Build Stage #
###############
......
......@@ -12,16 +12,19 @@ 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="
*\-p ,\--password \<Parameter>\ The password of the user.
\-url ,\--url \<Parameter>\ The URL of the host e.g. https://www.minds.com/ - defaults to use localhost.
\-h ,\--url \<Parameter>\ The URL of the host e.g. https://www.minds.com/ - defaults to use localhost.
\-u ,\--username \<Parameter>\ The username - defaults to cypress_e2e_test.
\-pu ,\--pro_username \<Parameter>\ The pro users username.
\-pp ,\--pro_password \<Parameter>\ The pro users password
\-v ,\---video \<Parameter>\ true if you want video providing.
\-e, \--env \<Parameter>\ add additional env variables e.g. production=true
"
if [ "$missing_required" != "" ]
......@@ -53,14 +56,16 @@ POSITIONAL=()
# set default arguments
url="http://localhost"
username="minds_cypress_tests"
pro_username="minds_pro_cypress_tests"
pro_password=""
env=""
_video=false
while [[ $# -gt 0 ]]
do
key="$1"
case $key in
-url|--url)
-h|--url)
url="$2"
shift 2
;;
......@@ -72,10 +77,22 @@ case $key in
password="$2"
shift 2
;;
-v|---video)
-v|--video)
_video="$2"
shift 2
;;
-pu|--pro-username)
pro_username="$2"
shift 2
;;
-pp|--pro-password)
pro_password="$2"
shift 2
;;
-e|--env)
env=",$2"
shift 2
;;
*)
POSITIONAL+=("$1") # saves unknown option in array
shift
......@@ -103,4 +120,5 @@ init_args $@
while [[ $PWD != '/' && ${PWD##*/} != 'front' ]]; do cd ..; done
#run cypress with args.
./node_modules/cypress/bin/cypress open --config baseUrl=$url,video=$_video --env username=$username,password=$password
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
$(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 => {
if (sessionCookie === null) {
return cy.login(true);
}
});
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
}
});
});
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 = "Test boost, 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);
}
});
newBoost(postContent, 500);
});
cy.visit('/newsfeed/subscriptions')
.location('pathname')
.should('eq', `/newsfeed/subscriptions`);
beforeEach(() => {
cy.server();
cy.route("POST", '**/api/v2/boost/**').as('boostPost');
cy.preserveCookies();
cy.visit('/boost/console/newsfeed/history');
});
after(() => {
cy.clearCookies();
});
it('should redirect a user to buy tokens when clicked', () => {
openTopModal();
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 5000 tokens', () => {
cy.post(postContent);
openTopModal();
cy.get('.m-boost--creator-section-amount input')
.type(5000);
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', () => {
openTopModal();
cy.get('.m-boost--creator-section-amount input')
.type(5000);
})
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', () => {
openTopModal();
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')
.first()
.click({force: true});
})
cy.get('[data-cy=data-minds-boost-creation-error]')
.contains(nonParticipationError);
});
function newBoost(text, views) {
cy.server();
cy.route("POST", '**/api/v2/boost/**').as('boostPost');
cy.visit('/newsfeed/subscribed');
cy.post(text);
function openTopModal() {
cy.get('#boost-actions')
.first()
.click()
.wait('@balance').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.deep.equal("success");
});
.click();
cy.get('.m-boost--creator-section-amount input')
.type(views);
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')
}
})
// Cannot test until env behaves consistently else,
// the test will frequently error when it cant see a boost.
context.skip('Boost Impressions', () => {
before(() => {
cy.getCookie('minds_sess')
.then((sessionCookie) => {
......@@ -27,9 +28,6 @@ context.skip('Boost Impressions', () => {
cy.reload();
})
// Cannot test until env behaves consistently.
// See: https://gitlab.com/minds/front/issues/1912
it('should register views on scroll', () => {
//smooth scroll
cy.scrollTo('0', '1%', { duration: 100 });
......
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();
......
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')";
......@@ -87,4 +87,4 @@ context('Comment Permissions', () => {
cy.get('minds-activity:first')
.find("i:contains('chat_bubble')");
});
});
\ No newline at end of file
});
......@@ -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(()=> {
......
......@@ -32,17 +32,21 @@ context('Groups', () => {
// click on hashtags dropdown
cy.get('m-hashtags-selector .m-dropdown--label-container').click();
// select #ART
cy.get('m-hashtags-selector m-dropdown m-form-tags-input > div > span').contains('#art').click();
cy.get('m-hashtags-selector m-dropdown m-form-tags-input > div > span').contains('art').click();
// type in another hashtag manually
cy.get('m-hashtags-selector m-form-tags-input input').type('hashtag{enter}').click();
// click away
cy.get('m-hashtags-selector .minds-bg-overlay').click();
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');
});
cy.get('.m-groupInfo__name').contains('test');
......
......@@ -52,6 +52,7 @@ context('Messenger', () => {
after(() => {
cy.deleteUser(testUsername, testPassword);
cy.clearCookies({log: true})
});
it('should allow a new user to set a password and send a message', () => {
......
......@@ -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();
});
});
......@@ -4,31 +4,25 @@
*/
import generateRandomId from '../support/utilities';
// Skipped until notifications are fixed on sandboxes
// See https://gitlab.com/minds/engine/issues/732
context.skip('Notification', () => {
//secondary user for testing.
let username = '';
let password = '';
const username = generateRandomId();
const password = generateRandomId()+'X#';
const commentText = 'test comment';
const postText = 'test comment'
const commentText = generateRandomId();
const postText = generateRandomId();
const postCommentButton = 'm-comment__poster > div > div.minds-body > div > div > a.m-post-button';
const commentButton = 'minds-activity > div.tabs > minds-button-comment > a';
const commentInput = 'm-comment__poster minds-textarea > div';
const commentContent ='.m-comment__bubble > p';
const notificationBell = 'm-notifications--topbar-toggle > a > i';
const notification = 'minds-notification';
/**
* Before all, generate username and password, login as the new user and log out.
* Next login to env user, make a post, and log out.
*/
before(() => {
username = generateRandomId();
password = generateRandomId()+'X#';
cy.newUser(username, password);
cy.logout();
......@@ -42,7 +36,9 @@ context.skip('Notification', () => {
*/
after(() => {
cy.clearCookies();
cy.login(true, username, password);
cy.visit(`/${Cypress.env().username}`);
cy.deleteUser(username, password);
});
......@@ -52,6 +48,8 @@ context.skip('Notification', () => {
* then switch users and check for the notification.
*/
beforeEach(() => {
cy.route("GET", '**/api/v1/notifications/all**').as('notifications');
cy.clearCookies();
cy.login(false, username, password);
......@@ -73,13 +71,16 @@ context.skip('Notification', () => {
cy.login();
// Open their notifications
cy.get(notificationBell).click();
cy.get(notificationBell).click()
.wait('@notifications').then((xhr) => {
expect(xhr.status).to.equal(200);
});
/**
* Notifications not working on test env.
* TODO: Check for notification - follow it
* through and check it leads to the post with postText.
*/
cy.get(notification)
.first()
.click();
cy.contains(commentText);
});
})
......@@ -43,28 +43,22 @@ context('Onboarding', () => {
.contains(welcomeText);
});
it('should allow a user to register', () => {
//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')
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();
});
it('should skip over subscribed channels', () => {
//click
cy.get(nextButton).click();
//TODO: Skipped over for now as subscribed channels is not working on staging environment.
cy.get(nextButton).click();
});
it('should let a user change their display name and description', () => {
cy.get(nameField).clear().type(name);
cy.get(descriptionfield).type(description);
cy.get(nextButton).click();
});
it('should allow a user to select their country', () => {
//set dialcode
cy.get(countryDropdown).click();
cy.get(ukOption).click();
......
This diff is collapsed.
This diff is collapsed.
......@@ -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();
......@@ -30,6 +31,7 @@ context('Registration', () => {
cy.location('pathname').should('eq', '/login');
cy.login(false, username, password);
cy.deleteUser(username, password);
cy.clearCookies();
})
it('should allow a user to register', () => {
......
......@@ -14,11 +14,11 @@ context('Remind', () => {
beforeEach(() => {
cy.preserveCookies();
cy.server();
cy.route("POST", "**/api/v2/newsfeed/remind/*").as("postRemind");
});
it('should allow a user to remind their post', () => {
cy.server();
cy.route("POST", "**/api/v2/newsfeed/remind/*").as("postRemind");
//post
cy.post("test!!");
......@@ -29,13 +29,14 @@ context('Remind', () => {
//fill out text box in modal
cy.get('.m-modal-remind-composer textarea')
.focus()
.clear()
.type(remindText);
//post remind.
cy.get('.m-modal-remind-composer-send i')
.click();
cy.wait('@postRemind').then((xhr) => {
.click()
.wait('@postRemind').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal("success");
});
......
......@@ -5,14 +5,25 @@ context('Subscription', () => {
const messageButton = 'm-messenger--channel-button > button';
const userDropdown = 'minds-button-user-dropdown > button';
beforeEach(()=> {
cy.login(true);
before(() => {
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
}
});
});
beforeEach(() => {
cy.preserveCookies();
cy.server();
cy.route("POST", "**/api/v1/subscribe/*").as("subscribe");
cy.route("DELETE", "**/api/v1/subscribe/*").as("unsubscribe");
cy.location('pathname', { timeout: 30000 })
.should('eq', `/newsfeed/subscriptions`);
cy.visit(`/${user}`);
})
cy.visit(`/${user}/`);
cy.location('pathname')
.should('eq', `/${user}/`);
});
it('should allow a user to subscribe to another', () => {
subscribe();
......@@ -20,16 +31,26 @@ context('Subscription', () => {
it('should allow a user to unsubscribe',() => {
unsubscribe();
})
});
function subscribe() {
cy.get(subscribeButton).click();
cy.get(messageButton).should('be.visible');
cy.get(subscribeButton)
.click()
.wait('@subscribe').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal("success");
});
cy.get(messageButton).should('be.visible')
}
function unsubscribe() {
cy.get(userDropdown).click();
cy.contains('Unsubscribe').click();
cy.contains('Unsubscribe')
.click()
.wait('@unsubscribe').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal("success");
});
cy.get(subscribeButton).should('be.visible');
}
......
......@@ -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', () => {
......
......@@ -5,28 +5,34 @@
* @desc Spec tests for Wire transactions.
*/
context('Wire', () => {
import generateRandomId from "../support/utilities";
// Issue to re-enable https://gitlab.com/minds/front/issues/1846
context.skip('Wire', () => {
const receiver = {
username: generateRandomId(),
password: generateRandomId()+'F!',
}
const sendAmount = 5000;
const wireButton = 'm-wire-channel > div > button';
const sendButton = '.m-wire--creator-section--last > div > button';
const modal = 'm-overlay-modal > div.m-overlay-modal';
before(() => {
cy.getCookie('minds_sess')
.then((sessionCookie) => {
if (sessionCookie === null) {
return cy.login(true);
}
});
cy.newUser(receiver.username, receiver.password);
cy.logout();
});
beforeEach(()=> {
cy.preserveCookies();
cy.login(true);
});
//TODO: Remove me when we get user to user wires working on the review environment
it.skip('should allow a user to send a wire to another user', () => {
// Visit users page.
cy.visit('/minds');
afterEach(() => {
// cy.login(true, receiver.username, receiver.password);
cy.visit(`/${Cypress.env().username}`);
// Click profile wire button
cy.get(wireButton).click();
......@@ -40,4 +46,19 @@ context('Wire', () => {
cy.get(modal).should('be.hidden');
});
it('should allow a user to send a wire to another user', () => {
// Visit users page.
cy.visit(`/${receiver.username}`);
// Click profile wire button
cy.get(wireButton).click();
cy.wait(2000);
// Click send button
cy.get(sendButton).click();
cy.wait(5000);
//Make sure modal is hidden after 5 seconds.
cy.get(modal).should('be.hidden');
});
})
......@@ -72,21 +72,23 @@ 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');
cy.server();
cy.route("POST", "/api/v1/authenticate").as("postLogin");
cy.get(loginForm.username).type(username);
cy.get(loginForm.password).type(password);
cy.get(loginForm.username).focus().type(username);
cy.get(loginForm.password).focus().type(password);
cy.get(loginForm.submit).click();
cy.wait('@postLogin').then((xhr) => {
cy.get(loginForm.submit)
.focus()
.click({force: true})
.wait('@postLogin').then((xhr) => {
expect(xhr.status).to.equal(200);
expect(xhr.response.body.status).to.equal('success');
});
......@@ -97,8 +99,7 @@ Cypress.Commands.add('login', (canary = false, username, password) => {
* @returns void
*/
Cypress.Commands.add('logout', () => {
cy.get(nav.hamburgerMenu).click();
cy.get(nav.logoutButton).click();
cy.visit('/logout')
});
/**
......@@ -111,9 +112,8 @@ Cypress.Commands.add('logout', () => {
* @returns void
*/
Cypress.Commands.add('newUser', (username = '', password = '') => {
cy.visit('/login');
cy.location('pathname', { timeout: 30000 })
cy.visit('/login')
.location('pathname')
.should('eq', `/login`);
cy.server();
......@@ -146,7 +146,7 @@ Cypress.Commands.add('newUser', (username = '', password = '') => {
});
Cypress.Commands.add('preserveCookies', () => {
Cypress.Cookies.preserveOnce('staging', 'minds_sess', 'mwa', 'XSRF-TOKEN');
Cypress.Cookies.preserveOnce('staging', 'minds_sess', 'mwa', 'XSRF-TOKEN', 'staging-features');
});
/**
......@@ -216,6 +216,17 @@ Cypress.Commands.add('post', (message) => {
});
});
/**
* Sets the feature flag cookie.
* @param { Object } flags - JSON object containing flags to turn on
* 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);
// });
/**
* Converts base64 to blob format
* @param { string } b64Data - The base64 data.
......
......@@ -247,7 +247,9 @@ export class EmbedImage {
if ($image.tagName === 'SPAN') {
$image = $image.parentNode.querySelector('img');
} else if ($image.tagName !== 'IMG') {
}
if (!$image || $image.tagName !== 'IMG') {
return;
}
......
......@@ -103,6 +103,13 @@
>
Error: You must upload a banner
</h1>
<h1
class="m-blog--edit--error"
i18n="@@M__COMMON__ERROR_GATEWAY_TIMEOUT"
*ngSwitchCase="'error:gateway-timeout'"
>
Error: Gateway Time-out
</h1>
<h1 class="m-blog--edit--error" *ngSwitchDefault>
Error: {{error}}
</h1>
......
......@@ -282,6 +282,7 @@ export class BlogEdit {
);
})
.catch(e => {
this.error = e;
this.canSave = true;
this.inProgress = false;
});
......
......@@ -278,6 +278,7 @@
*ngIf="showProSettings"
class="m-btn m-link-btn m-btn--with-icon m-btn--slim"
[routerLink]="proSettingsRouterLink"
data-cy="data-minds-sidebar-admin-pro-button"
>
<i class="material-icons">business_center</i>
<span i18n>Pro</span>
......
......@@ -184,7 +184,10 @@
[src]="[
{
res: '360',
uri: 'api/v1/media/' + comment.custom_data.guid + '/play',
uri:
'api/v1/media/' +
comment.custom_data.guid +
'/play?s=comment',
type: 'video/mp4'
}
]"
......
......@@ -356,9 +356,15 @@ export class CommentComponent implements OnChanges {
this.comment.modal_source_url = this.router.url;
this.overlayModal
.create(MediaModalComponent, this.comment, {
class: 'm-overlayModal--media',
})
.create(
MediaModalComponent,
{
entity: this.comment,
},
{
class: 'm-overlayModal--media',
}
)
.present();
}
}
......@@ -378,9 +378,15 @@ export class CommentComponentV2
this.comment.modal_source_url = this.router.url;
this.overlayModal
.create(MediaModalComponent, this.comment, {
class: 'm-overlayModal--media',
})
.create(
MediaModalComponent,
{
entity: this.comment,
},
{
class: 'm-overlayModal--media',
}
)
.present();
}
......
......@@ -296,7 +296,7 @@
height="300px"
[muted]="false"
[poster]="activity.custom_data.thumbnail_src"
[src]="[{ 'res': '360', 'uri': 'api/v1/media/' + activity.custom_data.guid + '/play', 'type': 'video/mp4' }]"
[src]="[{ 'res': '360', 'uri': 'api/v1/media/' + activity.custom_data.guid + '/play?s=activity', 'type': 'video/mp4' }]"
[guid]="activity.custom_data.guid"
[playCount]="activity['play:count']"
[torrent]="[{ res: '360', key: activity.custom_data.guid + '/360.mp4' }]"
......
......@@ -115,11 +115,11 @@ export class MindsVideoDirectHttpPlayer
return this.player.nativeElement;
}
play() {
async play() {
const player = this.getPlayer();
try {
player.play();
await player.play();
} catch (e) {
console.log(e);
}
......@@ -135,11 +135,11 @@ export class MindsVideoDirectHttpPlayer
}
}
toggle() {
async toggle() {
const player = this.getPlayer();
if (player.paused) {
this.play();
await this.play();
} else {
this.pause();
}
......
<ng-container *ngIf="current">
<m-video--direct-http-player
*ngIf="current.type === 'direct-http'"
*ngIf="current.type === 'direct-http' || true"
class="m-video--player"
[src]="current.src"
[poster]="poster"
......@@ -18,7 +18,7 @@
></m-video--direct-http-player>
<m-video--torrent-player
*ngIf="current.type === 'torrent'"
*ngIf="current.type === 'torrent' && false"
class="m-video--player"
[src]="current.src"
[poster]="poster"
......@@ -39,7 +39,7 @@
<i
*ngIf="
(!playerRef.isPlaying() && !playerRef.isLoading()) ||
(isActivity && metadataLoaded && !playerRef.isPlaying())
(shouldPlayInModal && !playerRef.isPlaying())
"
class="material-icons minds-video-play-icon"
(click)="clickedVideo()"
......
......@@ -435,9 +435,9 @@ export class MindsVideoComponent implements OnDestroy {
}
clickedVideo() {
if (!this.metadataLoaded) {
return;
}
// if (!this.metadataLoaded) {
// return;
// }
const isNotTablet = Math.min(screen.width, screen.height) < 768;
......
......@@ -56,7 +56,7 @@
[src]="[
{
res: '360',
uri: 'api/v1/media/' + entity.entity_guid + '/play',
uri: 'api/v1/media/' + entity.entity_guid + '/play?s=modal',
type: 'video/mp4'
}
]"
......
......@@ -170,6 +170,11 @@ m-overlay-modal {
m-video {
position: static;
video {
width: 100%;
height: 100%;
}
.minds-video-bar-full {
.m-video--progress-bar {
padding-right: 0;
......
......@@ -109,7 +109,7 @@ export class MediaModalComponent implements OnInit, OnDestroy {
routerSubscription: Subscription;
@Input('entity') set data(params: MediaModalParams) {
this.entity = JSON.parse(JSON.stringify(params.entity)); // deep clone
this.entity = params.entity && JSON.parse(JSON.stringify(params.entity)); // deep clone
this.redirectUrl = params.redirectUrl || null;
}
......@@ -144,13 +144,19 @@ export class MediaModalComponent implements OnInit, OnDestroy {
this.entity.title ||
`${this.entity.ownerObj.name}'s post`;
this.entity.guid = this.entity.entity_guid || this.entity.guid;
this.thumbnail = this.entity.thumbnails.xlarge;
this.thumbnail = this.entity.thumbnails
? this.entity.thumbnails.xlarge
: null;
switch (this.entity.custom_type) {
case 'video':
this.contentType = 'video';
this.entity.width = this.entity.custom_data.dimensions.width;
this.entity.height = this.entity.custom_data.dimensions.height;
this.entity.width = this.entity.custom_data.dimensions
? this.entity.custom_data.dimensions.width
: 1280;
this.entity.height = this.entity.custom_data.dimensions
? this.entity.custom_data.dimensions.height
: 720;
this.entity.thumbnail_src = this.entity.custom_data.thumbnail_src;
break;
case 'batch':
......
......@@ -13,7 +13,7 @@ export class MessengerSounds {
};
play(sound: string) {
if (this.canPlay()) this.sounds[sound].play();
// if (this.canPlay()) this.sounds[sound].play();
}
canPlay() {
......
......@@ -6,6 +6,8 @@ import {
Input,
OnDestroy,
OnInit,
Output,
EventEmitter,
ViewChild,
} from '@angular/core';
......@@ -18,6 +20,7 @@ import { DynamicHostDirective } from '../../../common/directives/dynamic-host.di
templateUrl: 'entity.component.html',
})
export class NewsfeedEntityComponent {
@Output() deleted = new EventEmitter<boolean>();
@ViewChild(DynamicHostDirective, { static: false })
host: DynamicHostDirective;
entity;
......@@ -65,4 +68,12 @@ export class NewsfeedEntityComponent {
componentRef.changeDetectorRef.detectChanges();
}
}
/**
* Sets entity to null and by extension hides it.
*/
delete(): void {
this.entity = null;
this.deleted.emit(true);
}
}
......@@ -12,7 +12,7 @@
[src]="[
{
res: '360',
uri: 'api/v1/media/' + entity.guid + '/play',
uri: 'api/v1/media/' + entity.guid + '/play?s=tile',
type: 'video/mp4'
}
]"
......
......@@ -69,7 +69,11 @@ export class Upload {
progress(100);
resolve(JSON.parse(this.response));
} else {
reject(this.response);
if (this.status === 504) {
reject('error:gateway-timeout');
} else {
reject(this.response);
}
}
};
xhr.onreadystatechange = function() {
......