Skip to content

Commit

Permalink
Working tests (#2)
Browse files Browse the repository at this point in the history
* Working tests

* Rearrange stuff
  • Loading branch information
jameswburke authored Aug 8, 2024
1 parent f67f410 commit 30bcb7a
Show file tree
Hide file tree
Showing 10 changed files with 5,894 additions and 543 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ yarn-debug.log*
yarn-error.log*

# local env files
.env
.env*.local*

# vercel
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20
148 changes: 148 additions & 0 deletions __checks__/helpers/capture-and-upload-screenshots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
const aws4 = require('aws4');
const axios = require('axios').default;
const { format } = require('date-fns');

const S3_BUCKET_NAME = process.env.PMC_SCREENSHOT_DASHBOARD_S3_BUCKET_NAME;
const AWS_REGION = process.env.PMC_SCREENSHOT_DASHBOARD_BUCKET_REGION;
const ACCESS_KEY_ID = process.env.PMC_SCREENSHOT_DASHBOARD_ACCESS_KEY_ID;
const SECRET_ACCESS_KEY = process.env.PMC_SCREENSHOT_DASHBOARD_SECRET_ACCESS_KEY;

// Define timestamp for folder.
const now = new Date();
const timestamp = format(now, 'yyyy-MM-dd-HH:mm:ss');

/**
* Capture screenshots and upload them to S3.
*
* @param {?} page
* @param {?string} url
*/
export async function captureAndUploadScreenshots ({
page,
url = null,
}) {

// Hide ads.
const urlObj = new URL( url );
urlObj.searchParams.set( 'skconfig', 's::true' );
url = urlObj.toString();

// Close OneTrust.
await page.addScriptTag({ content: 'window.OneTrust.Close();' });

const bauModal = await page.$( 'div.tp-modal .tp-close' );
if ( bauModal ) {
await bauModal.click();
}

await page.setViewportSize({ height: 1200, width: 1300 });
await page.goto(url || 'https://google.com' );
const desktopScreenshot = await page.screenshot({ fullPage: true, path: 'desktopScreenshot.jpg' });
await processScreenshot({
screenshot: desktopScreenshot,
name: 'desktop',
url,
});

await page.setViewportSize({ height: 1200, width: 800 });
await page.goto(url || 'https://google.com' );
const tabletScreenshot = await page.screenshot({ fullPage: true, path: 'tabletScreenshot.jpg' });
processScreenshot({
screenshot: tabletScreenshot,
name: 'tablet',
url,
});

await page.setViewportSize({ height: 1200, width: 400 });
await page.goto(url || 'https://google.com' );
const mobileScreenshot = await page.screenshot({ fullPage: true, path: 'mobileScreenshot.jpg' });
processScreenshot({
screenshot: mobileScreenshot,
name: 'mobile',
url,
});

// @todo Include the screenshot urls in this response?
return {
success: true,
};
}

/**
* Upload the screenshot to S3.
*
* @param {string} name Name of the file.
* @param {string} screenshot File data.
* @param {string} url URL for the screenshot.
*/
const processScreenshot = async ({
name,
screenshot,
url,
}) => {
const pathParts = getPathPartsByUrl(url);
if ( ! pathParts ) {
return false;
}

const { hostname, pathname } = pathParts;

const requestOptions = {
host: `${S3_BUCKET_NAME}.s3.${AWS_REGION}.amazonaws.com`,
method: 'PUT',
path: `/screenshots/${hostname}/${pathname}/${timestamp}/${name}.jpeg`,
body: screenshot,
headers: {
'Content-Type': 'image/jpeg',
},
};

const creds = {
accessKeyId: ACCESS_KEY_ID,
secretAccessKey: SECRET_ACCESS_KEY,
};

const opts = aws4.sign(requestOptions, creds);

await request(opts);

// If this fails, the aws4 library will throw an error. Otherwise assume
// success.
return true;
}

/**
* Fire the S3 upload request.
*
* @param {object} opts Request options.
*/
async function request(opts) {
try {
return await axios({
method: opts.method || 'GET',
url: `https://${opts.host}${opts.path}`,
headers: opts.headers || {},
data: opts.body || '',
});
} catch (error) {
console.log('Error', error);
return false;
}
}

/**
* Parse url and return a pathname and hostname sanitized for S3.
*
* @type {object|boolean}
*/
const getPathPartsByUrl = (url) => {
try {
const { hostname, pathname } = new URL(url);
return {
pathname: pathname.replace(/[^a-zA-Z0-9]/g, '_'),
hostname: hostname.replace(/[^a-zA-Z0-9]/g, '_'),
};
} catch (error) {
return false;
}
};
56 changes: 56 additions & 0 deletions __checks__/screenshots-group.check.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as path from 'path';
import { BrowserCheck, CheckGroup } from 'checkly/constructs';

/**
* Screenshots group.
*/
export const pmcWaybackMachineGroup = new CheckGroup( 'groups-pmc-wayback-machine', {
name: 'PMC Wayback Machine',
tags: [ 'screenshots', 'pmc-wayback-machine' ],
environmentVariables: [],
activated: true,
locations: [ 'us-east-1', 'eu-west-1' ],
concurrency: 100,
} );

/**
* Temporary list of urls copied from the yml.
*
* @todo Do we want to read the yml file or update it to use JSON?
*
* @type {Array}
*/
const urls = [
'https://variety.com',
'https://variety.com/2024/tv/entertainers/snl-marcello-hernandez-dave-chappelle-saturday-night-live-1236096469/',
'https://variety.com/v/film/',
'https://variety.com/search/?q=marvel',
'https://variety.com/author/michael-schneider/',
'https://footwearnews.com',
'https://footwearnews.com/2023/shop/best-sneakers-for-men-1203435445/',
'https://footwearnews.com/category/focus/athletic-outdoor/',
'https://footwearnews.com/brand/nike/',
'https://footwearnews.com/2023/business/earnings/nike-q3-earnings-2023-1203437116/',
];

/**
* Create a Checkly BrowserCheck for each url.
*/
for (const url of urls) {
const cleanedUrl = url
.replace('https://', '') // Remove protocol.
.replace('http://', '') // Remove protocol.
.replace(/[^a-zA-Z0-9]/g, '_'); // Replace unsupported characters.

new BrowserCheck(`screenshot-${cleanedUrl}`, {
name: `Screenshot: ${cleanedUrl}`,
locations: ['us-east-1', 'eu-west-1'],
group: pmcWaybackMachineGroup,
environmentVariables: [
{ key: 'SCREENSHOT_URL', value: url },
],
code: {
entrypoint: path.join(process.cwd(), '__checks__', 'screenshots-test.spec.ts'),
},
});
}
27 changes: 27 additions & 0 deletions __checks__/screenshots-test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { captureAndUploadScreenshots } from './helpers/capture-and-upload-screenshots';

const { expect, test } = require('@playwright/test');

// Configure the Playwright Test timeout to 210 seconds,
// ensuring that longer tests conclude before Checkly's browser check timeout of 240 seconds.
// The default Playwright Test timeout is set at 30 seconds.
// For additional information on timeouts, visit: https://checklyhq.com/docs/browser-checks/timeouts/
test.setTimeout(210000)

// Set the action timeout to 10 seconds to quickly identify failing actions.
// By default Playwright Test has no timeout for actions (e.g. clicking an element).
test.use({ actionTimeout: 10000 })

/**
* Use a test to run our screenshot logic.
*
* @todo Fail the test if the screenshot url 404s.
*/
test('wait for the url to load and capture screenshots', async ({ page }) => {

const url = process.env.SCREENSHOT_URL ?? '';
test.skip( 0 === url.length ); // No urls found.

const result = await captureAndUploadScreenshots({ page, url });
expect(result?.success, 'Screenshots did not capture successfully').toBe(true);
})
19 changes: 19 additions & 0 deletions checkly.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { defineConfig } from 'checkly';
import { BrowserCheck, Frequency } from 'checkly/constructs';

export default defineConfig({
projectName: 'PMC Wayback Machine',
logicalId: 'pmc-wayback-machine',
repoUrl: 'https://github.com/penske-media-corp/pmc-wayback-machine',
checks: {
activated: true,
muted: false,
runtimeId: '2024.02',
frequency: Frequency.EVERY_24H,
locations: [ 'us-east-1', 'eu-west-1' ],
checkMatch: '__checks__/**.check.ts',
},
cli: {
runLocation: 'eu-west-1',
},
});
Loading

0 comments on commit 30bcb7a

Please sign in to comment.