Front End Testing with GitHub Actions

Front End Testing with GitHub Actions

We acknowledge the traditional custodians of this land, the Gadigal people.

Front End Testing with GitHub Actions

Why test?

What about the front end?

The front end matters too

  • Accessibility Testing
  • Performance Testing
  • User Testing
  • HTML Validation
  • Visual Regression Testing

TLDR - You need a live website

Front end testing needs a front end

Why GitHub Actions?

Manual tests are 🤷‍♀️

Automation adds consistency

Why GitHub Actions?

github.com/features/actions
Workflow Files

.github/workflows/test.yml (yay yaml/yml 🙄)

Workflow → Jobs → Steps

name: Build and Test

on:
  pull_request:
	types: [opened, reopened, synchronize]
	branches: [prod]
on:
	create: # Create a branch or tag
	delete: # Delete a branch or tag
	deployment: # Create a deployment
	discussion: # Create/Update/Delete/etc a discussion
	fork: # Fork a repo
	issue_comment: # Create/Update/Delete a comment on an issue/PR
	project_card: # Create/Update/Delete/etc a project card
	push: # Push to a branch
	pull_request: # Create/Update/etc a PR
	push: # Push to a branch
	workflow_run: # Actions Workflow is Completed/Requested/In Progress
on:
  pull_request:
	types: [assigned, unassigned, labelled, unlabelled, opened, edited,
	closed, reopened, synchronize, ready_for_review, locked, unlocked,
	review_requested, review_request_removed, auto_merge_enabled, 
	auto_merge_disabled, ready_for_auto_merge]
	branches: [prod, development, feature/**]
	paths: ['**.js', '**.css']
on:
  pull_request:
	types: [opened, reopened, synchronize]
	branches: [prod]
name: Build and Test

on:
  pull_request:
	types: [opened, reopened, synchronize]
	branches: [prod]

jobs:
  build:
	runs-on: ubuntu-22.04
	steps:
	  - name: Checkout Repo Code
		uses: actions/checkout@v3

	  - name: Setup Node
		uses: actions/setup-node@v3
		with:
		  node-version: 18
- name: Setup Node
  uses: actions/setup-node@v3
  with:
	node-version: 16

- name: Run Custom Script
  run: ./.github/actions/my_script.sh
  env:
	MY_ENV_VAR: ${{ secrets.MY_ENV_VAR }}

- if: ${{ failure() }}
  uses: actions/github-script@v6
  with:
	script: github.rest.issues.createComment({
		issue_number: context.issue.number,
		body: 'Whoops, something went wrong'
	  })
- name: Checkout Repo Code
  uses: actions/checkout@v3

- name: Setup Node
  uses: actions/setup-node@v3
  with:
	node-version: 18

- name: Install Node Modules
  run: npm install
Repo → Actions
netlify.com

.github/actions/netlify_deploy.sh

#!/bin/bash

OUTPUT=$("netlify deploy --build --site ${SITE_ID} --auth ${TOKEN} --json")

NETLIFY_URL=$(jq -r '.deploy_url' <<<"${OUTPUT}")

echo "NETLIFY_URL=${NETLIFY_URL}" >> $GITHUB_OUTPUT
- name: Deploy to Netlify
  id: build_site
  env:
	TOKEN: ${{ secrets.TOKEN }}
	SITE_ID: ${{ secrets.SITE_ID }}
  run: ./.github/actions/netlify_deploy.sh

- uses: actions/github-script@v6
  with:
	script: |
	  github.rest.issues.createComment({
		issue_number: context.issue.number,
		owner: context.repo.owner,
		repo: context.repo.repo,
		body: 'URL: ${{steps.build_site.outputs.NETLIFY_URL}}'
	  })
- name: Deploy to Netlify
  id: build_site
  env:
	TOKEN: ${{ secrets.TOKEN }}
	SITE_ID: ${{ secrets.SITE_ID }}
  run: ./_actions/netlify_deploy.sh

- name: Deploy to Vercel
  uses: BetaHuhn/deploy-to-vercel-action@v1
  with:
    GITHUB_TOKEN: ${{ secrets.GH_PAT }}
    VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
    VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
    VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
- name: Deploy to Netlify
  id: build_site
  env:
	TOKEN: ${{ secrets.TOKEN }}
	SITE_ID: ${{ secrets.SITE_ID }}
  run: ./_actions/netlify_deploy.sh

- name: Build And Deploy
  id: azure_builddeploy
  uses: Azure/static-web-apps-deploy@v1
  with:
	azure_static_web_apps_api_token: ${{ secrets.AZURE_TOKEN }}
	repo_token: ${{ secrets.GITHUB_TOKEN }} # 
	action: "upload"
	app_location: "/"
	api_location: ""
	output_location: "dist"

Testing

jobs:
  build:
	runs-on: ubuntu-22.04
	outputs:
	  deploy_url: ${{steps.build_site.outputs.NETLIFY_URL}}
	steps:
	  # Previous build steps here

  test:
	runs-on: ubuntu-22.04
	needs: build
	steps:
	  - name: Checkout Repo Code
		uses: actions/checkout@v3
Evaluate and set job outputs
Warning: Skip output 'deploy_url' since it may contain secret.
Cleaning up orphan processes
	NETLIFY_SITE_ID=celebrated-gecko-4fec3c

	// Netlify URL:
	https://{commitHash}--celebrated-gecko-4fec3c.netlify.app

# .github/actions/netlify_deploy.sh

echo "NETLIFY_URL=${NETLIFY_URL}" >> $GITHUB_OUTPUT
echo "ENCODED_URL=$(echo $NETLIFY_URL | base64 -w0)"
  >> $GITHUB_OUTPUT
- name: Checkout Repo Code
  uses: actions/checkout@v3

- name: Decode URL
  run: |
	echo "DEPLOY_URL=$(echo "${{ needs.build.outputs.deploy_url }}" 
	| base64 --decode)" >> $GITHUB_OUTPUT

Lighthouse

- name: Decode URL
  id: decode_url
  run: |
	echo "DEPLOY_URL=$(echo "${{ needs.build.outputs.deploy_url }}" 
	| base64 --decode | base64 --decode)" >> $GITHUB_OUTPUT

- name: Audit URLs using Lighthouse
  uses: treosh/lighthouse-ci-action@v10
  with:
	urls: |
	  ${{ steps.decode_url.outputs.DEPLOY_URL }}
	uploadArtifacts: true

Playwright

- run: npm install && npx playwright install --with-deps

- name: Run Playwright tests
  env:
	BASE_URL: ${{ steps.decode_url.outputs.DEPLOY_URL }}
  run: npx playwright test

- uses: actions/upload-artifact@v3
  if: always()
  with:
	name: playwright-report
	path: playwright-report/
	retention-days: 30
- name: Run Playwright tests
  run: npx playwright test

- uses: actions/upload-artifact@v3
  if: always()
  with:
	name: playwright-report
	path: playwright-report/
	retention-days: 30

- uses: reggionick/s3-deploy@v4
  if: always()
  with:
	folder: playwright-report
	bucket: ${{ secrets.S3_BUCKET }}
	bucket-region: us-east-1

Accessibility Testing

pa11y.org
- name: Run Pa11y Tests
  env:
	BASE_URL: ${{ steps.decode_url.outputs.DEPLOY_URL }}
  run: pa11y-ci  --config .pa11y.json

Push to Production

name: Build and Publish

on:
  push:
	branches: [prod]

jobs:
  build:
	runs-on: ubuntu-22.04
	steps:
	  # Build steps here   
  - uses: actions/checkout@v3
  - name: Setup Node
	uses: actions/setup-node@v3
	with:
	  node-version: 20
  - run: npm install

  - name: Deploy to Netlify
	id: build_site
	env:
	  TOKEN: ${{ secrets.TOKEN }}
	  SITE_ID: ${{ secrets.SITE_ID }}
	run: ./.github/actions/netlify_deploy.sh --p true

Build Once, Deploy Many

  - uses: actions/checkout@v3
  - name: Setup Node
	uses: actions/setup-node@v3
	with:
	  node-version: 20
  - run: npm install

  - name: Deploy to Netlify
	id: build_site
	env:
	  TOKEN: ${{ secrets.TOKEN }}
	  SITE_ID: ${{ secrets.SITE_ID }}
	run: ./.github/actions/netlify_deploy.sh --p true
Repo → Actions
  • Build and Preview (anywhere)
  • Test live website
  • Build and Publish

Not just for testing

	// TODO: Will fix this later
	const myFunction = () => {
		...
	}
- name: Add Issues from Comments
  uses: "alstr/todo-to-issue-action@v4"
  with:
	AUTO_ASSIGN: true
	CLOSE_ISSUES: true

- name: Deploy to Netlify
  # Rest of build job
	# TODO: Fix prod command
	if [ "$prod" = "true" ]; then
		COMMAND="$COMMAND --prod"
	fi
- name: Add Issues from Comments
  uses: "alstr/todo-to-issue-action@v4"
  with:
	AUTO_ASSIGN: true
	CLOSE_ISSUES: true
  • Create tasks/issues for comments/bugs
  • Lint/validate code
  • Check for broken links
  • Wait for an approval
  • Generate assets
  • Repo Admin
github.com/amykapernick/front-end-testing
blog.amyskapers.dev/front-end-testing-with-github-actions
kapers.dev/fender-testing

Thank you 👏