Front End Testing with GitHub Actions

Front End Testing with GitHub Actions

Why test?

kapers.dev/fender-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
  id: 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

COMMAND="netlify deploy --build --site ${SITE_ID} --auth ${TOKEN} --json"

OUTPUT=$($COMMAND)

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
Settings → Secrets and variables → Actions
- 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}}'
	  })
Settings → Actions → Workflow Permissions
kapers.dev/deploy-preview
- 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

Lighthouse

- name: Audit URLs using Lighthouse
  uses: treosh/lighthouse-ci-action@v10
  with:
    urls: |
      ${{ needs.build.outputs.deploy_url }}
    uploadArtifacts: true
Evaluate and set job outputs
Warning: Skip output 'deploy_url' since it may contain secret.
Cleaning up orphan processes
# .github/actions/netlify_deploy.sh

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

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

- run: echo ${{ steps.decode_url.outputs.DEPLOY_URL }}
- 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
- 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: 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
COMMAND="netlify deploy --build --site ${SITE_ID} --auth ${TOKEN} --json"

OUTPUT=$($COMMAND)

COMMAND="netlify deploy --build --site ${SITE_ID} --auth ${TOKEN} --json"

while getopts p: flag
do
	case "${flag}" in
		p) prod=${OPTARG};;
	esac
done

if [ "$prod" = "true" ]; then
	COMMAND="$COMMAND --prod"
fi

OUTPUT=$($COMMAND)

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 👏