Skip to content

Commit

Permalink
Merge pull request #4497 from Countly/SER-822
Browse files Browse the repository at this point in the history
[SER-822] Added ui-tests into community
  • Loading branch information
ar2rsawseen authored Sep 6, 2023
2 parents 8d2104f + 9cfa801 commit 417a579
Show file tree
Hide file tree
Showing 21 changed files with 2,484 additions and 24 deletions.
26 changes: 25 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,31 @@
"mocha": true
},
"parserOptions": {
"ecmaVersion": 2020
"ecmaVersion": 2020,
"sourceType": "module"
},
"rules": {
"no-console": "off",
"no-unused-vars": "off",
"no-redeclare": "off"
}
},
{
"files": [
"ui-tests/**/*.js"
],
"env": {
"es6": true,
"node": true,
"mocha": true
},
"globals": {
"Cypress": "readonly",
"cy": "readonly"
},
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"rules": {
"no-console": "off",
Expand Down
66 changes: 54 additions & 12 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ jobs:
- name: Remove plugin tests
shell: bash
run: rm -rf /opt/countly/test/4.plugins

- name: Configure API
shell: bash
run: "sed -i 's/port: 3001,/port: 3001, workers: 1,/' /opt/countly/api/config.js"
Expand Down Expand Up @@ -194,7 +194,7 @@ jobs:
- name: Prepare tests
shell: bash
run: bash bin/scripts/countly.prepare.ce.plugins.tests.sh

- name: Configure API
shell: bash
run: "sed -i 's/port: 3001,/port: 3001, workers: 1,/' /opt/countly/api/config.js"
Expand All @@ -207,7 +207,7 @@ jobs:
npm install
node bin/scripts/test.connection.js
npx grunt mochaTest
test-e2e:
ui-test:
runs-on: ubuntu-latest

services:
Expand All @@ -227,11 +227,15 @@ jobs:
COUNTLY_CONFIG__MONGODB_HOST: mongodb

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2

- name: Prepare tests
- name: Install Chrome
shell: bash
run: bash bin/scripts/countly.prepare.ce.tests.sh
run: |
apt update
apt install -y libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb wget
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb -O /tmp/chrome.deb
apt install -y /tmp/chrome.deb
- name: Copy code
shell: bash
Expand All @@ -240,15 +244,53 @@ jobs:
- name: Prepare files to use correct MongoDB host
shell: bash
run: "sed -i 's/mongosh --quiet/mongosh --host mongodb --quiet/' /opt/countly/bin/backup/run.sh && sed -i 's/mongoimport --db/mongoimport --host mongodb --db/' /opt/countly/bin/backup/run.sh"

- name: Configure API
shell: bash
run: "sed -i 's/port: 3001,/port: 3001, workers: 1,/' /opt/countly/api/config.js"

- name: Run tests
- name: Prepare environment
shell: bash
working-directory: /opt/countly
run: |
sed -i 's/port: 3001,/port: 3001, workers: 1,/' /opt/countly/api/config.js
cp "./plugins/plugins.default.json" "/opt/countly/plugins/plugins.json"
npm install
sudo countly task dist-all
bash bin/backup/run.sh
bash bin/scripts/countly.prepare.ce.tests.sh
countly add_user '${{ secrets.CYPRESS_USER_EMAIL }}' '${{ secrets.CYPRESS_USER_PASSWORD }}'
mongosh --host mongodb --eval 'db.getSiblingDB("countly").members.updateOne({username: "${{ secrets.CYPRESS_USER_EMAIL }}"}, {$set: {username: "${{ secrets.CYPRESS_USER_USERNAME }}"}});'
cd ui-tests
mkdir cypress/fixtures
echo '{"username": "${{ secrets.CYPRESS_USER_USERNAME }}","email": "${{ secrets.CYPRESS_USER_EMAIL }}","password": "${{ secrets.CYPRESS_USER_PASSWORD }}"}' > cypress/fixtures/user.json
sed -i 's/00000000-0000-0000-0000-000000000000/${{ secrets.CYPRESS_KEY }}/g' package.json
cp cypress.config.sample.js cypress.config.js
sed -i 's/000000/${{ secrets.CYPRESS_PROJECT_ID }}/g' cypress.config.js
- name: Run UI tests
shell: bash
working-directory: /opt/countly
run: |
/sbin/my_init &
cd ui-tests
npm install
node bin/scripts/test.connection.js
npm run cy:run
- name: Upload UI tests artifacts
if: ${{ failure() }}
shell: bash
working-directory: /opt/countly/ui-tests/cypress
run: |
ARTIFACT_ARCHIVE_NAME="$(date '+%Y%m%d-%H.%M')_${GITHUB_REPOSITORY#*/}_CI#${{ github.run_number }}.tar.gz"
tar zcvf "$ARTIFACT_ARCHIVE_NAME" screenshots videos hars
curl -o /tmp/uploader.log -u "${{ secrets.BOX_UPLOAD_AUTH }}" ${{ secrets.BOX_UPLOAD_PATH }} -T "$ARTIFACT_ARCHIVE_NAME"
notify:
runs-on: ubuntu-latest
needs: [ install, lint, test-api-core, test-api-plugins ]
steps:
- name: Send slack notification
id: slack
if: always()
uses: slackapi/[email protected]
with:
payload: "{\"url\":\"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\",\"status\":\"${{ job.status }}\"}"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
8 changes: 0 additions & 8 deletions bin/backup/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,9 @@ check_connectivity mongosh
# Backup is taken with the following commands
#
# /usr/bin/mongoexport --db countly --collection app_crashgroups58bf06bd6cba850047ac9f19 --out app_crashgroups58bf06bd6cba850047ac9f19.json
# /usr/bin/mongoexport --db countly --collection app_users58bf06bd6cba850047ac9f19 --out app_users58bf06bd6cba850047ac9f19.json
# /usr/bin/mongoexport --db countly --collection app_viewdata58bf06bd6cba850047ac9f19 --out app_viewdata58bf06bd6cba850047ac9f19.json
# /usr/bin/mongoexport --db countly --collection apps --out apps.json
# /usr/bin/mongoexport --db countly --collection jobs --out jobs.json
# /usr/bin/mongoexport --db countly --collection members --out members.json
# /usr/bin/mongoexport --db countly --collection sessions_ --out sessions_.json

/usr/bin/mongoimport --db countly --collection app_crashgroups58bf06bd6cba850047ac9f19 --file app_crashgroups58bf06bd6cba850047ac9f19.json --upsert
/usr/bin/mongoimport --db countly --collection app_users58bf06bd6cba850047ac9f19 --file app_users58bf06bd6cba850047ac9f19.json --upsert
/usr/bin/mongoimport --db countly --collection app_viewdata58bf06bd6cba850047ac9f19 --file app_viewdata58bf06bd6cba850047ac9f19.json --upsert
/usr/bin/mongoimport --db countly --collection apps --file apps.json --upsert
/usr/bin/mongoimport --db countly --collection jobs --file jobs.json --upsert
/usr/bin/mongoimport --db countly --collection members --file members.json --upsert
/usr/bin/mongoimport --db countly --collection sessions_ --file sessions_.json --upsert
2 changes: 1 addition & 1 deletion bin/scripts/countly.prepare.ce.plugins.tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ set +e
NODE_JS_CMD=$(which nodejs)
set -e
if [[ -z "$NODE_JS_CMD" ]]; then
ln -s "$(which node)" /usr/bin/nodejs
ln -s "$(which node)" /usr/bin/nodejs
elif [ ! -f "/usr/bin/node" ]; then
ln -s "$(which nodejs)" /usr/bin/node
fi
4 changes: 2 additions & 2 deletions bin/scripts/countly.prepare.ce.tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ set +e
NODE_JS_CMD=$(which nodejs)
set -e
if [[ -z "$NODE_JS_CMD" ]]; then
ln -s "$(which node)" /usr/bin/nodejs
ln -s "$(which node)" /usr/bin/nodejs
elif [ ! -f "/usr/bin/node" ]; then
ln -s "$(which nodejs)" /usr/bin/node
fi
fi
6 changes: 6 additions & 0 deletions ui-tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
cypress/hars
cypress/videos
cypress/screenshots
cypress/fixtures/user.json
cypress.config.js
56 changes: 56 additions & 0 deletions ui-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Countly UI Automation / CYPRESS

- Technology Used : JavaScript
- Framework Used: Cypress

### Installation

- Firstly, clone the project with ssh to the local repo
- Then install all dependencies `npm install`
- Copy config sample and configure as you need `cp cypress.config.sample.js cypress.config.js`

### Running Tests

- Run `npm run cy:open`: Open the Cypress Test Runner
- Run `npm run cy:run`: Run Cypress tests to completion
- Run `npm run cy:run:login`: Run Cypress with login feature

### Dependencies

[Cypress](https://docs.cypress.io/)

- `npm install cypress`

### Structure of Automation

#### e2e

- Includes test cases of features. Feature files are under the `cypress/e2e/{featureFolder}` and called as `{featureName}.cy.js`

Exp; `cypress/e2e/analytics/loyalty/userActivity.cy.js`

#### Lib

//TO DO

#### Support

//TO DO

#### Fixtures

//TO DO

#### Commands

- Command file is under the `cypress/support` folder and the file has global commands

### Selector Priority //TO DO

- `“data-test-id”` is the first preference

```bash
Cypress.SelectorPlayground.defaults({
selectorPriority: ['data-test-id', 'id', 'class']
})
```
18 changes: 18 additions & 0 deletions ui-tests/cypress.config.sample.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { defineConfig } = require("cypress");

module.exports = defineConfig({
e2e: {
baseUrl: "http://localhost",
defaultCommandTimeout: 30000,
viewportWidth: 2000,
viewportHeight: 1100,
numTestsKeptInMemory: 0,
projectId: "000000",
chromeWebSecurity: false,
watchForFileChanges: true,
retries: {
runMode: 0,
openMode: 0,
},
},
});
16 changes: 16 additions & 0 deletions ui-tests/cypress/e2e/analytics/loyalty/userActivity.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import user from '../../../fixtures/user.json';
const loginHelpers = require('../../../lib/login');
const navigationHelpers = require('../../../support/navigations');
const analyticsLoyaltyUserActivityHelpers = require('../../../lib/analytics/loyalty/userActivity');

describe('Visitor Activity', () => {
beforeEach(function() {
navigationHelpers.goToLoginPage();
});

it('should be visible all elements on empty state', function() {
loginHelpers.login(user.username, user.password);
navigationHelpers.goToVisitorLoyalty();
analyticsLoyaltyUserActivityHelpers.verifyEmptyPageElements();
});
});
34 changes: 34 additions & 0 deletions ui-tests/cypress/e2e/login.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import user from '../fixtures/user.json';
const loginHelpers = require('../lib/login');
const navigationHelpers = require('../support/navigations');

describe('Login', () => {
beforeEach(function() {
navigationHelpers.goToLoginPage();
});

it('should be visible all elements on empty state', function() {
loginHelpers.verifyEmptyPageElements();
});

it('should successfully log in to with valid username and password', function() {
loginHelpers.typeUsername(user.username);
loginHelpers.typePassword(user.password);
loginHelpers.clickLoginButton();
navigationHelpers.isNavigatedToDashboard();
});

it('should successfully log in to with valid e-mail and password', function() {
loginHelpers.typeUsername(user.email);
loginHelpers.typePassword(user.password);
loginHelpers.clickLoginButton();
navigationHelpers.isNavigatedToDashboard();
});

it('should display an error message with invalid password', function() {
loginHelpers.typeUsername(user.email);
loginHelpers.typePassword('invalidpassword');
loginHelpers.clickLoginButton();
loginHelpers.verifyLoginFailedMessage();
});
});
10 changes: 10 additions & 0 deletions ui-tests/cypress/lib/analytics/loyalty/userActivity.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import analyticsLoyaltyUserActivityPageElements from "../../../support/elements/analytics/loyalty/userActivity";

const verifyEmptyPageElements = () => {
cy.shouldBeVisible(analyticsLoyaltyUserActivityPageElements.VISITOR_ACTIVITY_LABEL);
cy.shouldContainText(analyticsLoyaltyUserActivityPageElements.VISITOR_ACTIVITY_LABEL, "User Activity");
};

module.exports = {
verifyEmptyPageElements,
};
59 changes: 59 additions & 0 deletions ui-tests/cypress/lib/login.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import loginPageElements from '../support/elements/login';

const typeUsername = username => {
cy.typeInput(loginPageElements.USERNAME, username);
};

const typePassword = password => {
cy.typeInput(loginPageElements.PASSWORD, password);
};

const clickLoginButton = () => {
cy.clickElement(loginPageElements.LOGIN_BUTTON);
};

const verifyLoginFailedMessage = () => {
cy.shouldBeVisible(loginPageElements.NOTIFICATION_IMAGE);
cy.shouldContainText(loginPageElements.FAILED_MESSAGE, 'Login Failed');
};

const verifyUnvalidUsernameOrEmailMessage = () => {
cy.shouldBeVisible(loginPageElements.USERNAME_ERROR_MESSAGE);
cy.shouldContainText(loginPageElements.USERNAME_ERROR_MESSAGE, 'Please enter a valid username or email.');
};

const verifyEmptyPageElements = () => {
cy.shouldBeVisible(loginPageElements.LOGO);
cy.shouldBeVisible(loginPageElements.LANGUAGE);
cy.shouldBeVisible(loginPageElements.SIGN_IN_LABEL);
cy.shouldContainText(loginPageElements.SIGN_IN_LABEL, "Sign In");
cy.shouldBeVisible(loginPageElements.USERNAME);
cy.shouldPlaceholderContainText(loginPageElements.USERNAME, "Username or Email");
cy.shouldBeVisible(loginPageElements.PASSWORD);
cy.shouldPlaceholderContainText(loginPageElements.PASSWORD, "Enter your password");
cy.shouldBeVisible(loginPageElements.FORGOT_PASSWORD_LINK);
cy.shouldContainText(loginPageElements.FORGOT_PASSWORD_LINK, "Forgot password?");
cy.shouldHrefContainUrl(loginPageElements.FORGOT_PASSWORD_LINK, "./forgot");
cy.shouldBeVisible(loginPageElements.LOGIN_BUTTON);
cy.shouldHaveValue(loginPageElements.LOGIN_BUTTON, "Sign In");
cy.shouldBeVisible(loginPageElements.FOOTER);

cy.clickElement(loginPageElements.LOGIN_BUTTON);
verifyUnvalidUsernameOrEmailMessage();
};

const login = (username, password) => {
typeUsername(username);
typePassword(password);
clickLoginButton();
};

module.exports = {
typeUsername,
typePassword,
clickLoginButton,
verifyLoginFailedMessage,
verifyUnvalidUsernameOrEmailMessage,
verifyEmptyPageElements,
login,
};
Loading

0 comments on commit 417a579

Please sign in to comment.