Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAuth migration | Add E2E tests for OAuth authentication #1274

Merged
merged 7 commits into from
Dec 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
raphaelkabo marked this conversation as resolved.
Show resolved Hide resolved
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
raphaelkabo marked this conversation as resolved.
Show resolved Hide resolved
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
andrewHEguardian marked this conversation as resolved.
Show resolved Hide resolved
cy.wait(1000);
kelvin-chappell marked this conversation as resolved.
Show resolved Hide resolved

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
Loading