Skip to content

Commit

Permalink
Merge pull request #1274 from guardian/rk/e2e-cypress-okta
Browse files Browse the repository at this point in the history
OAuth migration | Add E2E tests for OAuth authentication
  • Loading branch information
raphaelkabo authored Dec 18, 2023
2 parents 901f8b1 + 52c60b9 commit 4715bec
Show file tree
Hide file tree
Showing 31 changed files with 535 additions and 95 deletions.
66 changes: 66 additions & 0 deletions .github/workflows/cypress-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: manage-frontend cypress (E2E)
on:
pull_request:
branches:
- main
workflow_dispatch:

jobs:
cypress:
name: Manage-frontend Cypress (E2E)
runs-on: ubuntu-latest

permissions:
id-token: write
contents: read

steps:
- uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ secrets.MANAGE_FRONTEND_IAM_ROLE }}
aws-region: eu-west-1

- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
cache: yarn

- name: Setup OS, Nginx, and Certs
run: |
sudo apt-get update -y
sudo apt-get install -y libnss3-tools
sudo service nginx restart
wget -q https://github.com/FiloSottile/mkcert/releases/download/v1.4.3/mkcert-v1.4.3-linux-amd64
wget -q https://github.com/guardian/dev-nginx/releases/latest/download/dev-nginx.tar.gz
sudo cp mkcert-v1.4.3-linux-amd64 /usr/local/bin/mkcert
sudo chmod +x /usr/local/bin/mkcert
sudo mkdir -p /usr/local/bin/dev-nginx
sudo tar -xzf dev-nginx.tar.gz -C /usr/local
sudo chmod +x /usr/local/bin/dev-nginx
sudo dev-nginx setup-cert "profile.thegulocal.com"
sudo dev-nginx setup-cert "manage.thegulocal.com"
sudo dev-nginx setup-cert "members-data-api.thegulocal.com"
sudo cp ./cypress/cypress-nginx.conf /etc/nginx/nginx.conf
sudo dev-nginx restart-nginx
- name: Cypress run
uses: cypress-io/github-action@v6
env:
CYPRESS_IDAPI_CLIENT_ACCESS_TOKEN: ${{ secrets.IDAPI_CLIENT_ACCESS_TOKEN }}
# This env variable prevents Node from rejecting self-signed TLS certificates. It's
# required for the Cypress tests to run as we're unable to verify the created certs.
# See: https://nodejs.org/api/cli.html#node_tls_reject_unauthorizedvalue
NODE_TLS_REJECT_UNAUTHORIZED: 0
with:
start: yarn cypress:e2e:server
wait-on: 'https://manage.thegulocal.com'
wait-on-timeout: 30
quiet: true
browser: chrome
spec: cypress/tests/e2e/*.cy.ts

- uses: actions/upload-artifact@v3
if: failure()
with:
name: cypress-screenshots
path: cypress/screenshots
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
name: manage-frontend cypress
name: manage-frontend cypress (mocked)
on:
push:
branches-ignore:
- 'dependabot/**'

jobs:
cypress:
name: Manage-frontend Cypress
name: Manage-frontend Cypress (mocked)
runs-on: ubuntu-latest

permissions:
Expand All @@ -31,12 +31,11 @@ jobs:

- name: Cypress run
uses: cypress-io/github-action@v6
env:
IDAPI_CLIENT_ACCESS_TOKEN: ${{ secrets.IDAPI_CLIENT_ACCESS_TOKEN }}
with:
start: yarn cypress:server
start: yarn cypress:mocked:server
wait-on: 'http://localhost:9234, http://localhost:9233'
wait-on-timeout: 30
quiet: true
browser: chrome
spec: cypress/e2e/parallel-${{ matrix.group }}/*.ts
spec: cypress/tests/mocked/parallel-${{ matrix.group }}/*.cy.ts
config: baseUrl=http://localhost:9234
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ manage-frontend.zip
test-report.xml

cypress/screenshots
cypress.env.json
7 changes: 2 additions & 5 deletions cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ export default defineConfig({
viewportWidth: 1500,
viewportHeight: 860,
video: false,
failOnStatusCode: false,
chromeWebSecurity: false,
experimentalSessionSupport: true,
blockHosts: [
'*ophan.theguardian.com',
'pixel.adsafeprotected.com',
Expand All @@ -26,11 +24,10 @@ export default defineConfig({
openMode: 0,
},
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
specPattern: 'cypress/tests/**/*.cy.{js,jsx,ts,tsx}',
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.ts')(on, config);
},
baseUrl: 'http://localhost:9234/',
baseUrl: 'https://manage.thegulocal.com',
},
});
134 changes: 134 additions & 0 deletions cypress/cypress-nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# NGINX Conf file used by Cypress-Nginx Github actions
# so we can run cypress tests against the local nginx server
# with a ssl cert set on the domain

#user nobody;
worker_processes 1;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;

sendfile on;

# Set to 5 seconds longer than 60 seconds (pretty sure this is a magic numnber).
# This should help prevent timeouts in Cypress requests inside Github Actions.
keepalive_timeout 65;

# fixes issues for large response headers
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;


# manage.thegulocal.com
# ======================
server {
listen 443 ssl;
server_name manage.thegulocal.com;
proxy_http_version 1.1; # this is essential for chunked responses to work

ssl_certificate manage.thegulocal.com.crt;
ssl_certificate_key manage.thegulocal.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

location / {
proxy_pass http://localhost:9234/;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}

# members-data-api.thegulocal.com
# ======================
server {
listen 443 ssl;
server_name members-data-api.thegulocal.com;
proxy_http_version 1.1; # this is essential for chunked responses to work

ssl_certificate members-data-api.thegulocal.com.crt;
ssl_certificate_key members-data-api.thegulocal.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

location / {
proxy_pass https://members-data-api.code.dev-theguardian.com;
proxy_next_upstream error timeout http_404 non_idempotent;
proxy_set_header "X-Forwarded-Proto" "https";
proxy_set_header Host members-data-api.code.dev-theguardian.com;
proxy_set_header Accept-Encoding "";
proxy_hide_header Content-Security-Policy;

proxy_cookie_domain members-data-api.code.dev-theguardian.com members-data-api.thegulocal.com;
proxy_cookie_domain .code.dev-theguardian.com .thegulocal.com;

sub_filter_types application/json;
sub_filter_once off;
sub_filter 'members-data-api.code.dev-theguardian.com' 'members-data-api.thegulocal.com';
}
}


# profile.thegulocal.com
# ======================
server {
listen 443 ssl;
server_name profile.thegulocal.com;
proxy_http_version 1.1; # this is essential for chunked responses to work

ssl_certificate profile.thegulocal.com.crt;
ssl_certificate_key profile.thegulocal.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;

# dummy location header for the API
proxy_set_header X-GU-ID-Geolocation ip:$remote_addr,country:GB,city:Leeds;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

location / {
proxy_pass https://profile.code.dev-theguardian.com;
proxy_next_upstream error timeout http_404 non_idempotent;
proxy_set_header "X-Forwarded-Proto" "https";
proxy_set_header "X-GU-Okta-Env" "profile.code.dev-theguardian.com";
proxy_set_header Host profile.code.dev-theguardian.com;
proxy_set_header Accept-Encoding "";
proxy_hide_header Content-Security-Policy;

proxy_cookie_domain profile.code.dev-theguardian.com profile.thegulocal.com;
proxy_cookie_domain .code.dev-theguardian.com .thegulocal.com;

sub_filter_types application/json;
sub_filter_once off;
sub_filter 'profile.code.dev-theguardian.com' 'profile.thegulocal.com';

######
# remove `sid` cookie in requests to Gateway
# save original "Cookie" header value
set $altered_cookie $http_cookie;
# check if the "sid" cookie is present
# From: https://stackoverflow.com/a/67627604
if ($http_cookie ~ '(.*)(^|;\s)sid=("[^"]*"|[^\s]*[^;]?)(\2|$|;$)(?:;\s)?(.*)') {
# cut "sid" cookie from the string
set $altered_cookie $1$4$5;
}
# hide original "Cookie" header
proxy_hide_header Cookie;
# set "Cookie" header to the new value
proxy_set_header Cookie $altered_cookie;
######
}
}
}
24 changes: 24 additions & 0 deletions cypress/lib/signInOkta.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Non-mocked sign-in with Gateway using Okta
*/

export const signInOkta = () => {
// When this function runs, the browser will already be showing the Gateway sign-in page
// because MMA will have redirected to it when it loads the first page of the test.
cy.setCookie('gu-cmp-disabled', 'true', {
domain: '.thegulocal.com',
});

// Necessary otherwise we get a 502 error back from MMA for some reason, perhaps a race condition?
// TODO: Make this not suck
cy.wait(1000);

cy.visit('/');
cy.createTestUser({
isUserEmailValidated: true,
})?.then(({ emailAddress, finalPassword }) => {
cy.get('input[name=email]').type(emailAddress);
cy.get('input[name=password]').type(finalPassword);
cy.get('[data-cy="main-form-submit-button"]').click();
});
};
10 changes: 10 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ Cypress.Commands.add('iframeLoaded', { prevSubject: 'element' }, ($iframe) => {
});
});

Cypress.Commands.add('solveGoogleReCAPTCHA', () => {
cy.get('#recaptcha *> iframe').then(($iframe) => {
const $body = $iframe.contents().find('body');
cy.wrap($body)
.find('.recaptcha-checkbox-border')
.should('be.visible')
.click();
});
});

Cypress.Commands.add('resolve', (name, options = {}) => {
const getValue = () => {
// @ts-ignore
Expand Down
1 change: 1 addition & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ declare global {
resolve(name: string): Chainable<Element>;
getIframeBody(selector: string): Chainable<Element>;
findByText(text: string): Chainable<Element>;
solveGoogleReCAPTCHA(): Chainable<Element>;
}
}
}
Expand Down
78 changes: 78 additions & 0 deletions cypress/tests/e2e/e2e.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { signInOkta } from '../../lib/signInOkta';

describe('E2E with Okta', () => {
beforeEach(() => {
Cypress.config('defaultCommandTimeout', 30_000);

cy.clearAllCookies();
signInOkta();
cy.get('h1').should('contain', 'Account overview');
});

context('account overview tab', () => {
it('should contain an email address', () => {
cy.findByText('Email address');
});
});

context('profile tab', () => {
it('should contain a username', () => {
cy.visit('/public-settings');
cy.findByText('Username');
});
});

context('emails tab', () => {
it('should allow the user to update their email preferences', () => {
cy.visit('/email-prefs');
cy.findByText('Guardian products and support').click();
cy.visit('/email-prefs');
cy.findByText('Guardian products and support')
.parents('div')
.get('input[type="checkbox"]')
.should('be.checked');
});

it('should allow the user to unsubscribe from all emails', () => {
cy.visit('/email-prefs');
cy.findByText('Unsubscribe from all emails').click();
cy.visit('/email-prefs');
cy.get('input[type="checkbox"]').should('not.be.checked');
});
});

context('settings tab', () => {
it('should allow the user to update their personal information', () => {
cy.visit('/account-settings');
cy.findByLabelText('First Name').clear();
cy.findByLabelText('Last Name').clear();
cy.findByLabelText('First Name').type('Test');
cy.findByLabelText('Last Name').type('User');
cy.findByText('Save changes').click();
cy.visit('/account-settings');
cy.findByLabelText('First Name').should('have.value', 'Test');
cy.findByLabelText('Last Name').should('have.value', 'User');
});
});

context('help tab', () => {
it('should allow the user to submit the help centre contact form', () => {
cy.visit('/help');
cy.findByText(
'If you still can’t find what you need and want to contact us, check',
)
.parent()
.findByText('here')
.click();
cy.findByText('Take me to the form').click();
cy.findByText('Begin form').click();
cy.findByText('Continue to step 2').click();
cy.findByLabelText('Full Name').type('Test');
cy.get('textarea[name="message"]').type('Problem details');
cy.solveGoogleReCAPTCHA();
cy.wait(1000);
cy.findByText('Submit').click();
cy.findByText('Thank you for contacting us');
});
});
});
Loading

0 comments on commit 4715bec

Please sign in to comment.