Set up end to end tests (#121)

* initial e2e test setup with jest and puppeteer

* Update package-lock.json

* WIP e2e setup

* finish setting up end to end tests

* extend open addon time

* build local version and change background color

* update pink image snapshot

* hide no "done" callback jest lint error

* first draft of integration test CI automation

* add .env file during CI

* add puppeteer and update .env

* try mac-os

* try separate build step

* add local deploy step

* extend time

* print body text

* set user agent

* rearrange console logs

* remove console logs

* console page

* try loading in own window

* install mkcert in CI

* fix indentation

* test local body text

* bump time

* revert to loading in add on

* bump time

* print page body

* add stealth plugin and update puppeteer

* update jest and jest-puppeteer versions to latest

* use custom jest-puppeteer environment

allows connecting to Google account using stealth plugin

* update threshold

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* test console

* switch to clasp 2.3.1

* increase timeout

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update package.json

* try mac os 10.15

* Update integration-tests.yaml

* run node 12,14

* Update global-teardown.js

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* sudo mkcert

* Update integration-tests.yaml

* try running not in bash

* try windows 2016

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update open-addon.js

* Update open-addon.js

* Update open-addon.js

* Update open-addon.js

* Update open-addon.js

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* add timeout

* test recovery email

* bypass account recovery page

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* test New-SelfSignedCertificate

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* add powershell script

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* Update integration-tests.yaml

* test script

* Update integration-tests.yaml

* add generate-cert.ps1

* restructure test files

* Update generate-cert.ps1

* bump failure threshold

* fix install mkcert

* remove consoles

* Update package.json

* Update integration-tests.yaml

* try forceExit flag

* test exec in global setup

* restructure

* Update integration-tests.yaml

* remove ?. null check

* add 4 OSs: macos-10.15, macos-latest, windows-2019, windows-latest

* test mac 10.15

* Update integration-tests.yaml

* use all OSs

* -Keyexportpolicy Exportable

* Update generate-cert.ps1

* Update integration-tests.yaml

* update os

* -KeyExportPolicy 'Exportable'

* Update integration-tests.yaml

* update test readme

* Update integration-tests.yaml

* add non-extended integration test

* add non-extended snapshots

* update snapshot names

* change name

* add basic integration tests pipeline file

* Update local-development.test.js

* update os's

* test s3 image reporter

* allowSizeMismatch: true

* update test local url to localhost:3000/dialog-demo-bootstrap.html

* try medium font render hinting

* Update jest-puppeteer.config.js

* update screenshots

* check resolution

* open page when wds is ready

* change screen resolution

* change to 2560 1440 resolution

* test 790 width

* change back to 800

* test all mac versions

* rename container

* skip s3 image reporting if missing env variables

* try windows

* add OS specific tests

* fix image names

* Update image-reporter.js

* Update package.json

* upload windows

* Update image-reporter.js

* test new font

* try all OSs

* remove OS-specific test setup

* update extended snapshots

* rename tests

* Update integration-tests-extended.yaml

* Update integration-tests-basic.yaml

* extend timeout

* Update jest.config.js

* Update README.md

* Update local-development.test.js

* revert to 0.04 threshold

* add comment

* remove push trigger for basic test

* extend timeout
This commit is contained in:
Elisha Nuchi
2022-03-12 22:51:30 -05:00
committed by GitHub
parent b14c0f9219
commit 89c12fa161
24 changed files with 13377 additions and 4295 deletions
@@ -0,0 +1,43 @@
name: Local integration tests - Basic Version
on:
pull_request:
branches: [master]
jobs:
basic-integration-test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-10.15, macos-latest, windows-latest]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
node-version: [12, 14, 16]
timeout-minutes: 8
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: Install packages [npm ci]
run: npm ci
- name: Allow installing cert in Mac OS Big Sur
run: sudo security authorizationdb write com.apple.trust-settings.admin allow
if: matrix.os == 'macOS-latest'
- name: Install mkcert
run: brew install mkcert
if: runner.os == 'MacOS'
- name: Run mkcert setup [mkcert -install]
run: mkcert -install
if: runner.os == 'MacOS'
- name: Install https cert [npm setup:https]
run: npm run setup:https
if: runner.os == 'MacOS'
- run: |
mkdir certs
.\test\generate-cert.ps1
shell: pwsh
if: runner.os == 'Windows'
- name: Run integration tests
run: npm run test:integration
shell: bash
@@ -0,0 +1,72 @@
name: Local integration tests - Extended Version
on:
push:
jobs:
extended-integration-test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-10.15, macos-latest, windows-latest]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
node-version: [12, 14, 16]
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: Install packages [npm ci]
run: npm ci
- name: Allow installing cert in Mac OS Big Sur
run: sudo security authorizationdb write com.apple.trust-settings.admin allow
if: matrix.os == 'macOS-latest'
- name: Install mkcert
run: brew install mkcert
if: runner.os == 'MacOS'
- name: Run mkcert setup [mkcert -install]
run: mkcert -install
if: runner.os == 'MacOS'
- name: Install https cert [npm setup:https]
run: npm run setup:https
if: runner.os == 'MacOS'
- run: |
mkdir certs
.\test\generate-cert.ps1
shell: pwsh
if: runner.os == 'Windows'
- name: Add .clasprc.json to home folder
run: echo "$DOT_CLASPRC" > $HOME/.clasprc.json
shell: bash
env:
DOT_CLASPRC: ${{ secrets.DOT_CLASPRC }}
- name: Add .clasp.json to project directory
run: echo "$DOT_CLASP" > .clasp.json
shell: bash
env:
DOT_CLASP: ${{ secrets.DOT_CLASP }}
- name: Add environment variables to .env file
run: |
echo "EMAIL=$TEST_ACCOUNT_EMAIL" > .env
echo "TEST_RECOVERY_EMAIL=$TEST_RECOVERY_EMAIL" >> .env
echo "PASSWORD=$TEST_ACCOUNT_PASSWORD" >> .env
echo "SHEET_URL=$TEST_SPREADSHEET_URL" >> .env
echo "S3_BUCKET_NAME=$S3_BUCKET_NAME" >> .env
echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" >> .env
echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" >> .env
shell: bash
env:
TEST_ACCOUNT_EMAIL: ${{ secrets.TEST_ACCOUNT_EMAIL }}
TEST_RECOVERY_EMAIL: ${{ secrets.TEST_RECOVERY_EMAIL }}
TEST_ACCOUNT_PASSWORD: ${{ secrets.TEST_ACCOUNT_PASSWORD }}
TEST_SPREADSHEET_URL: ${{ secrets.TEST_SPREADSHEET_URL }}
S3_BUCKET_NAME: ${{ secrets.S3_BUCKET_NAME }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
- name: Build and deploy dev setup [npm run deploy:dev]
run: npm run deploy:dev
- name: Run integration tests
run: npm run test:integration:extended
shell: bash
+3
View File
@@ -14,3 +14,6 @@ dist/
# mac
.DS_Store
#secret
.env
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
globalSetup: './test/global-setup.js',
globalTeardown: './test/global-teardown.js',
testEnvironment: './test/puppeteer-environment.js',
reporters: ['default', '<rootDir>/test/utils/image-reporter.js'],
};
+12749 -4293
View File
File diff suppressed because it is too large Load Diff
+13 -2
View File
@@ -7,7 +7,8 @@
"url": "https://github.com/enuchi/React-Google-Apps-Script.git"
},
"scripts": {
"test": "test",
"test:integration": "jest --forceExit test/local-development.test",
"test:integration:extended": "cross-env IS_EXTENDED=1 jest --forceExit test/local-development.test",
"login": "clasp login",
"setup": "rimraf .clasp.json && clasp create --type sheets --title \"My React Project\" --rootDir ./dist",
"open": "clasp open --addon",
@@ -50,7 +51,11 @@
"@babel/polyfill": "^7.10.4",
"@babel/preset-env": "^7.11.0",
"@babel/preset-react": "^7.10.4",
"@google/clasp": "^2.4.0",
"@google/clasp": "^2.3.1",
"@types/expect-puppeteer": "^4.4.6",
"@types/jest-environment-puppeteer": "^4.4.1",
"@types/puppeteer": "^5.4.4",
"aws-sdk": "^2.1092.0",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-add-module-exports": "^1.0.2",
@@ -69,6 +74,7 @@
"eslint-plugin-babel": "^5.3.1",
"eslint-plugin-googleappsscript": "^1.0.3",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-jest": "^24.3.6",
"eslint-plugin-jsx-a11y": "^6.3.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.1.4",
@@ -81,9 +87,14 @@
"google-apps-script-webpack-dev-server": "^1.0.0",
"html-webpack-inline-source-plugin": "0.0.10",
"html-webpack-plugin": "^3.2.0",
"jest": "^27.5.1",
"jest-image-snapshot": "^4.5.1",
"mkdirp": "^1.0.4",
"module-to-cdn": "^3.1.5",
"prettier": "^1.19.1",
"puppeteer": "^13.4.1",
"puppeteer-extra": "^3.2.3",
"puppeteer-extra-plugin-stealth": "^2.9.0",
"rimraf": "^3.0.2",
"style-loader": "^1.2.1",
"tern": "^0.24.3",
@@ -1,3 +1,8 @@
/* needed to make consistent test snapshots across OSs */
body {
font-family: Arial, Helvetica, sans-serif !important;
}
/*
CSSTransitionGroup styling
*/
+36
View File
@@ -0,0 +1,36 @@
{
"root": true,
"parser": "babel-eslint",
"extends": [
"airbnb-base",
"plugin:prettier/recommended",
"plugin:jest/recommended"
],
"plugins": ["babel", "prettier"],
"env": {
"browser": true,
"es6": true,
"jest": true
},
"globals": {
"page": true,
"browser": true
},
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"jest/no-done-callback": "off",
"prettier/prettier": "error",
"camelcase": "warn",
"import/prefer-default-export": "warn",
"import/no-extraneous-dependencies": "warn",
"jest/expect-expect": "off",
"no-new": "off",
"no-underscore-dangle": "off"
}
}
+49
View File
@@ -0,0 +1,49 @@
# Running tests
Integration tests here are used to ensure the existing local development setup works across different platforms, and that new features do not break the functionality of the local development environment.
The "extended" local development integration test builds and deploys the local development setup needed to use hot reload functionality. It deploys the local setup to a spreadsheet inside a test Google account, runs a local development server, and checks that changes to the local files are reflected inside the spreadsheet's add-on, similar to how local development is intended to be used.
Jest is used to run the tests and compare images of the changes, and Puppeteer is used to control the browser. A test account and test spreadsheet need to be created and credentials need to be passed in through environment variables in order for this "extended" version to work. Example `.env` file variables are listed below. Since secret values cannot be used in PRs opened from forks, a "basic" integration test just loads the local development setup locally, instead of deploying to Google Sheets.
## Extended integration test
In order to test the extended integration test locally you will need to create or use a test Google account. It's recommended you create an account solely for this purpose rather than using an existing account. You'll need to create a new project and include the following variables in the `.env` file in the root:
```bash
SHEET_URL = https://docs.google.com/spreadsheets/d/****************/edit#gid=0
EMAIL = myisolatedtestaccount@gmail.com
PASSWORD = testaccountpassword
TEST_RECOVERY_EMAIL = myisolatedtestaccountsrecoveryemail@gmail.com
# For image reporting only
S3_BUCKET_NAME = my-bucket-name
AWS_ACCESS_KEY_ID = A35D************
AWS_SECRET_ACCESS_KEY = slkf***********/234jdh**********
```
Make sure that you have followed the instructions to install local certs (see main README). The test will use Puppeteer to open the spreadsheet URL and log in to your account using your email and password provided. In case it doesn't recognize you it will ask for this account's recovery email -- you'll need to include this in the `.env` file as well.
### Running the extended tests in the pipeline
To run the extended integration test in a pipeline using Github Actions, reference the .github/workflows directory. You'll need to add some additional environment variables through the repo secrets settings in order to get the full flow working:
DOT_CLASPRC - the `.clasprs.json` file from user directory containing clasp login credentials
DOT_CLASP - the `.clasp.json` file from this repo matching the test spreadsheet
TEST_ACCOUNT_EMAIL, TEST_RECOVERY_EMAIL, TEST_ACCOUNT_PASSWORD, TEST_SPREADSHEET_URL - see above variables
# Additional notes
### Stealth login
`puppeteer-extra` and `puppeteer-extra-plugin-stealth` are used to help log in to a Google account in the pipeline for the "extended" integration test. They are not easily compatible with the `jest-puppeteer` package, so a custom integration is used that is documented on `jest`'s site [here](jestjs.io/docs/puppeteer#custom-example-without-jest-puppeteer-preset).
### Generating certs in the pipeline
There are permissions issues running `mkcert -install` in Windows runners, so a powershell script in `./generate-cert.ps1` is used to generate the certs and enable https.
### Jest open handles
There are issues with `jest` keeping handles open after all tests finish on Windows runners, so `jest --forceExit` is used to resolve them, although this flag may not be needed on certain platforms (like Mac OS).
### Jest Image Snapshots
`jest-image-snapshot` is used to compare browser snapshots and detect discrepancies or regressions. Thresholds have been adjusted to allow for some variation across different platforms due to differences in font rendering and color profiles.
If a snapshot fails, a diff image is created in the __diff_output__ directory, showing the comparison. If it fails in the pipeline it may be hard to see this diff. An Image Reporter class is used to upload diff images to an S3 bucket from the pipeline. This can be modified to use any image store. If used, make sure it is enabled in `jest.config.js` and that the necessary environment variables (AWS keys, bucket name, etc.) are added to `.env` file (see above section).
Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

+40
View File
@@ -0,0 +1,40 @@
# reference code: https://stackoverflow.com/questions/70226493/webpack-dev-server-and-https-this-site-can-t-be-reached
# reference issue: https://github.com/FiloSottile/mkcert/issues/286
$dnsName = "localhost"
$expiry = [DateTime]::Now.AddYears(1);
$repoRoot = Split-Path $PSScriptRoot
$certsDir = "$repoRoot\certs";
$fileName = "cert.pfx";
$passwordText = "abc123";
$name = "ReactApp";
Write-Host "Creating cert directly into CurrentUser\My store"
$certificate = New-SelfSignedCertificate `
-KeyExportPolicy 'Exportable' `
-CertStoreLocation Cert:\CurrentUser\My `
-Subject $name `
-FriendlyName $name `
-DnsName $dnsName `
-NotAfter $expiry
$certFile = Join-Path $certsDir $fileName
Write-Host "Exporting certificate to $certFile"
$password = ConvertTo-SecureString `
-String $passwordText `
-Force -AsPlainText
Export-PfxCertificate `
-Cert $certificate `
-FilePath $certFile `
-Password $password | Out-Null
Write-Host "Importing $certFile to CurrentUser\Root store for immediate system wide trust"
Import-PfxCertificate `
-FilePath $certFile `
-CertStoreLocation Cert:\LocalMachine\Root `
-Password $password | Out-Null
+26
View File
@@ -0,0 +1,26 @@
// Use custom jest puppeteer preset as described here:
// jestjs.io/docs/puppeteer#custom-example-without-jest-puppeteer-preset
// This allows using stealth mode.
const { mkdir, writeFile } = require('fs').promises;
const os = require('os');
const path = require('path');
const puppeteer = require('puppeteer-extra');
// add stealth plugin and use defaults (all evasion techniques)
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
const jestPuppeteerConfig = require('./jest-puppeteer.config');
module.exports = async function() {
puppeteer.use(StealthPlugin());
const browser = await puppeteer.launch(jestPuppeteerConfig.launch);
// store the browser instance so we can teardown it later
// this global is only available in the teardown but not in TestEnvironments
global.__BROWSER_GLOBAL__ = browser;
// use the file system to expose the wsEndpoint for TestEnvironments
await mkdir(DIR, { recursive: true });
await writeFile(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint());
};
+16
View File
@@ -0,0 +1,16 @@
// Use custom jest puppeteer preset as described here:
// jestjs.io/docs/puppeteer#custom-example-without-jest-puppeteer-preset
// This allows using stealth mode.
const fs = require('fs').promises;
const os = require('os');
const path = require('path');
const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
module.exports = async function() {
// close the browser instance
await global.__BROWSER_GLOBAL__.close();
// clean-up the wsEndpoint file
await fs.rmdir(DIR, { recursive: true, force: true });
};
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
launch: {
headless: false,
product: 'chrome',
args: [
'--force-color-profile=generic-rgb',
'--font-render-hinting=none',
'--disable-font-subpixel-positioning',
'--enable-font-antialiasing',
'--disable-gpu',
],
},
browserContext: 'default',
};
+105
View File
@@ -0,0 +1,105 @@
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const { configureToMatchImageSnapshot } = require('jest-image-snapshot');
const { openAddon } = require('./utils/open-addon');
require('dotenv').config();
const isExtended = process.env.IS_EXTENDED == true;
const toMatchImageSnapshot = configureToMatchImageSnapshot({
failureThreshold: 0.04,
failureThresholdType: 'percent',
customDiffConfig: {
threshold: 0.1,
},
blur: 2,
allowSizeMismatch: true,
});
expect.extend({ toMatchImageSnapshot });
jest.setTimeout(120000);
const srcTestFile = path.join(
__dirname,
'../src/client/dialog-demo-bootstrap/components/SheetEditor.jsx'
);
const webpackDevServerReady = async process => {
return new Promise(resolve => {
process.stdout.on('data', data => {
if (data.includes('Compiled successfully.')) resolve();
});
});
};
describe(`Local setup ${isExtended ? '*extended*' : ''}`, () => {
let page;
let process;
const containerSelector = isExtended ? '.script-app-dialog' : 'body';
beforeAll(async () => {
process = exec('npm run serve');
page = await global.__BROWSER_GLOBAL__.newPage();
await page.setViewport({
width: 800,
height: 800,
deviceScaleFactor: 1,
});
if (isExtended) {
await openAddon(page);
} else {
await webpackDevServerReady(process);
await page.goto('https://localhost:3000/dialog-demo-bootstrap.html');
await page.waitForTimeout(3000);
}
});
afterAll(() => {
process.kill();
});
it('should load Bootstrap example', async () => {
const container = await page.$(containerSelector);
const image = await container.screenshot();
await expect(image).toMatchImageSnapshot();
});
it('should modify Bootstrap title example', async () => {
const data = await fs.promises.readFile(srcTestFile, 'utf8');
const result = data
.replace(
'<b>☀️ Bootstrap demo! ☀️</b>',
'<b>☀️ This is modified text in local development ☀️</b>'
)
.replace(
"{ padding: '3px', overflowX: 'hidden' }",
"{ padding: '3px', overflowX: 'hidden', backgroundColor: 'black', color: 'white' }"
);
await fs.promises.writeFile(srcTestFile, result, 'utf8');
await page.waitForTimeout(4000);
const container = await page.$(containerSelector);
const image = await container.screenshot();
await expect(image).toMatchImageSnapshot();
});
it('should modify Bootstrap title example back to original', async () => {
const data = await fs.promises.readFile(srcTestFile, 'utf8');
const result = data
.replace(
'<b>☀️ This is modified text in local development ☀️</b>',
'<b>☀️ Bootstrap demo! ☀️</b>'
)
.replace(
"{ padding: '3px', overflowX: 'hidden', backgroundColor: 'black', color: 'white' }",
"{ padding: '3px', overflowX: 'hidden' }"
);
await fs.promises.writeFile(srcTestFile, result, 'utf8');
await page.waitForTimeout(4000);
const container = await page.$(containerSelector);
const image = await container.screenshot();
await expect(image).toMatchImageSnapshot();
});
});
+41
View File
@@ -0,0 +1,41 @@
// Use custom jest puppeteer preset as described here:
// jestjs.io/docs/puppeteer#custom-example-without-jest-puppeteer-preset
// This allows using stealth mode.
const { readFile } = require('fs').promises;
const os = require('os');
const path = require('path');
const puppeteer = require('puppeteer');
const NodeEnvironment = require('jest-environment-node');
const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup');
class PuppeteerEnvironment extends NodeEnvironment {
constructor(config) {
super(config);
}
async setup() {
await super.setup();
// get the wsEndpoint
const wsEndpoint = await readFile(path.join(DIR, 'wsEndpoint'), 'utf8');
if (!wsEndpoint) {
throw new Error('wsEndpoint not found');
}
// connect to puppeteer
this.global.__BROWSER_GLOBAL__ = await puppeteer.connect({
browserWSEndpoint: wsEndpoint,
});
}
async teardown() {
await super.teardown();
}
getVmContext() {
return super.getVmContext();
}
}
module.exports = PuppeteerEnvironment;
+74
View File
@@ -0,0 +1,74 @@
/* eslint-disable */
/**
* Reference: https://github.com/americanexpress/jest-image-snapshot/blob/main/examples/image-reporter.js
*
* To enable this image reporter, add it to your `jest.config.js` "reporters" definition:
* "reporters": [ "default", "<rootDir>/image-reporter.js" ]
*
* Image Reporter may not work with jest's --forceExit flag
*/
const fs = require('fs');
const AWS = require('aws-sdk/global');
const S3 = require('aws-sdk/clients/s3'); // this is needed
require('dotenv').config();
const UPLOAD_BUCKET = process.env.S3_BUCKET_NAME;
const s3 = new AWS.S3({ apiVersion: '2006-03-01' });
class ImageReporter {
constructor(globalConfig, options) {
this._globalConfig = globalConfig;
this._options = options;
}
onTestResult(test, testResult, aggregateResults) {
if (
testResult.numFailingTests &&
testResult.failureMessage.match(/different from snapshot/)
) {
if (
!process.env.S3_BUCKET_NAME ||
!process.env.AWS_SECRET_ACCESS_KEY ||
!process.env.AWS_ACCESS_KEY_ID
) {
console.log(
'Missing env variables. Skipping upload of image diff files.'
);
return;
}
const targetDirectories = [
'./test/__image_snapshots__/',
'./test/__image_snapshots__/__diff_output__/',
];
targetDirectories.forEach(targetDirectory => {
fs.readdirSync(targetDirectory, { withFileTypes: true }).forEach(
dirent => {
if (!dirent.isFile()) return;
const path = `images/${dirent.name}`;
const params = {
Body: fs.readFileSync(`${targetDirectory}/${dirent.name}`),
Bucket: UPLOAD_BUCKET,
Key: path,
ContentType: 'image/png',
};
s3.putObject(params, err => {
if (err) {
console.log(err, err.stack);
} else {
console.log(
`Uploaded file to https://${UPLOAD_BUCKET}.s3.amazonaws.com/${path}`
);
}
});
}
);
});
}
}
}
module.exports = ImageReporter;
+74
View File
@@ -0,0 +1,74 @@
const openAddon = async page => {
await page.goto(process.env.SHEET_URL);
await page.waitForTimeout(5000);
await page.click('a:nth-child(2)'); // click on signin button
await page.waitForTimeout(3000);
await page.type('input[name="identifier"]', process.env.EMAIL); // type email
await page.click('#identifierNext'); // click "next" button
await page.waitForTimeout(3000);
await page.type('input[name="password"]', process.env.PASSWORD); // type pass
await page.waitForTimeout(3000);
await page.click('#passwordNext'); // click "next" button
await page.waitForTimeout(3000);
if (
await page.evaluate(
() =>
document.querySelector('h1#headingText') &&
document.querySelector('h1#headingText').innerText.includes('erify')
)
) {
try {
await page.click('div[data-accountrecovery]');
await page.waitForTimeout(6000);
} catch {
console.log('The "choose account recovery method" page isn\'t shown');
}
await page.type(
'input[name="knowledgePreregisteredEmailResponse"]',
process.env.TEST_RECOVERY_EMAIL
); // type recovery email
await page.waitForTimeout(6000);
await page.click('button'); // click "next" button
await page.waitForTimeout(5000);
}
await page.waitForTimeout(25000); // wait long enough for onopen to be called
// open new addon menubar item
await page.evaluate(() => {
const addOnMenuButton = document.querySelector(
'div.menu-button.goog-control.goog-inline-block:nth-child(11)'
);
addOnMenuButton.dispatchEvent(
new MouseEvent('mousedown', { bubbles: true })
);
addOnMenuButton.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
});
await page.waitForTimeout(6000);
// open "bootstrap" menu item
await page.evaluate(() => {
const bootstrapMenuButton = document.querySelector(
'div.goog-menu.goog-menu-vertical.apps-menu-hide-mnemonics:last-child > div:nth-child(2) > div'
);
bootstrapMenuButton.dispatchEvent(
new MouseEvent('mousedown', { bubbles: true })
);
bootstrapMenuButton.dispatchEvent(
new MouseEvent('mouseup', { bubbles: true })
);
bootstrapMenuButton.dispatchEvent(
new MouseEvent('mousedown', { bubbles: true })
);
bootstrapMenuButton.dispatchEvent(
new MouseEvent('mouseup', { bubbles: true })
);
});
await page.waitForTimeout(10000);
};
module.exports = { openAddon };
+11
View File
@@ -66,6 +66,7 @@ const clientEntrypoints = [
// see "npm run setup:https" script in package.json
const keyPath = path.resolve(__dirname, './certs/key.pem');
const certPath = path.resolve(__dirname, './certs/cert.pem');
const pfxPath = path.resolve(__dirname, './certs/cert.pfx'); // if needed for Windows
/*********************************
* Declare settings
@@ -224,6 +225,16 @@ if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {
};
}
// If mkcert -install cannot be used on Windows machines (in pipeline, for example), the
// script at test/generate-cert.ps1 can be used to create a .pfx cert
if (fs.existsSync(pfxPath)) {
// use pfx file if it's found
devServer.https = {
pfx: fs.readFileSync(pfxPath),
passphrase: 'abc123',
};
}
// webpack settings for the development client wrapper
const devClientConfigs = clientEntrypoints.map(clientEntrypoint => {
envVars.FILENAME = clientEntrypoint.filename;