diff --git a/Puppeteer/Chrome-Recorder/.gitignore b/Puppeteer/Chrome-Recorder/.gitignore new file mode 100644 index 0000000..8f5e467 --- /dev/null +++ b/Puppeteer/Chrome-Recorder/.gitignore @@ -0,0 +1,3 @@ +node_modules +package-lock.json +.env \ No newline at end of file diff --git a/Puppeteer/Chrome-Recorder/README.md b/Puppeteer/Chrome-Recorder/README.md new file mode 100644 index 0000000..26162ab --- /dev/null +++ b/Puppeteer/Chrome-Recorder/README.md @@ -0,0 +1,13 @@ +An example to extend [Puppeteer Replay](https://goo.gle/puppeteer-replay) to use [Applitools](https://applitools.com/) to capture screenshots, validate and replay the exported user flow JSON file from Chrome DevTools Recorder. + +Here is the blog post: https://applitools.com/blog/creating-first-test-google-chrome-devtools-recorder/ + +# To run the demo + +1. Install [Nodejs](https://nodejs.org/en/) +2. Run `npm install` to install the dependencies. +3. Sign up for an Applitools account (https://applitools.com/) and get the API Key. +4. Replace the `apiKey` in `main.mjs` (or create an `.env` file with `APPLITOOLS_API_KEY`). +5. Run `npm start` to start the demo. +6. See the Applitools test result in the dashboard (https://eyes.applitools.com/app/test-results/). +7. Read the [blog post](https://applitools.com/blog/creating-first-test-google-chrome-devtools-recorder/) to understand what is happening. \ No newline at end of file diff --git a/Puppeteer/Chrome-Recorder/applitools.config.mjs b/Puppeteer/Chrome-Recorder/applitools.config.mjs new file mode 100644 index 0000000..571e38a --- /dev/null +++ b/Puppeteer/Chrome-Recorder/applitools.config.mjs @@ -0,0 +1,22 @@ +import { BrowserType, DeviceName } from '@applitools/eyes-puppeteer'; + +export async function setupEyes(eyes, batchName, apiKey) { + eyes.setApiKey(apiKey); + + const configuration = eyes.getConfiguration(); + + configuration.setBatch({ name: batchName }) + configuration.setStitchMode('CSS'); + + // Add browser + configuration.addBrowser({ width: 1200, height: 800, name: BrowserType.CHROME }); + configuration.addBrowser({ width: 1200, height: 800, name: BrowserType.FIREFOX }); + configuration.addBrowser({ width: 1200, height: 800, name: BrowserType.SAFARI }); + configuration.addBrowser({ width: 1200, height: 800, name: BrowserType.EDGE_CHROMIUM }); + configuration.addBrowser({ width: 1200, height: 800, name: BrowserType.IE_11 }); + configuration.addBrowser({ deviceName: DeviceName.Pixel_2 }); + configuration.addBrowser({ deviceName: DeviceName.iPhone_X }); + + eyes.setConfiguration(configuration); +}; + \ No newline at end of file diff --git a/Puppeteer/Chrome-Recorder/main.mjs b/Puppeteer/Chrome-Recorder/main.mjs new file mode 100644 index 0000000..73eaaa5 --- /dev/null +++ b/Puppeteer/Chrome-Recorder/main.mjs @@ -0,0 +1,63 @@ +import 'dotenv/config'; +import url from 'url'; +import { createRunner, parse, PuppeteerRunnerExtension } from '@puppeteer/replay'; +import { setupEyes } from './applitools.config.mjs'; +import puppeteer from 'puppeteer'; +import fs from 'fs'; +import { Eyes, Target, VisualGridRunner } from '@applitools/eyes-puppeteer'; + +// Extend runner to take screenshot after each step +class Extension extends PuppeteerRunnerExtension { + async afterEachStep(step, flow) { + await super.afterEachStep(step, flow); + await eyes.check(`recording step: ${step.type}`, Target.window().fully(false)); + console.log(`after step: ${step.type}`); + } +} + +// Puppeteer: launch browser +const browser = await puppeteer.launch({ headless: false }); // or set it to true +const page = await browser.newPage(); + +// Aplitools: launch visual grid runner & eyes +const visualGridRunner = new VisualGridRunner({ testConcurrency: 5 }); +const eyes = new Eyes(visualGridRunner); + +const apiKey = process.env.APPLITOOLS_API_KEY || 'REPLACE_YOUR_APPLITOOLS_API_KEY'; +const name = 'Chrome Recorder Demo'; + +await setupEyes(eyes, name, apiKey); +await eyes.open(page, { + appName: 'Order a coffee', + testName: 'My First Applitools Chrome Recorder test!', + visualGridOptions: { ieV2: true } +}); + +// Puppeteer: read the JSON user flow +const recordingText = fs.readFileSync('./order-a-coffee.json', 'utf8'); +const flow = parse(JSON.parse(recordingText)); + +// Puppeteer: Replay the script +export async function run(extension) { + const runner = await createRunner(flow, extension); + await runner.run(); +} + +if (process && import.meta.url === url.pathToFileURL(process.argv[1]).href) { + await run(new Extension(browser, page, 7000)); +} + +// Puppeteer: clean up +await browser.close(); + +// Applitools: clean up +await eyes.closeAsync(); +await eyes.abortAsync(); // abort if Eyes were not properly closed + +// Applitools: Manage tests across multiple Eyes instances +const testResultsSummary = await visualGridRunner.getAllTestResults() +for (const testResultContainer of testResultsSummary) { + const testResults = testResultContainer.getTestResults(); + console.log(testResults); +} + diff --git a/Puppeteer/Chrome-Recorder/order-a-coffee.json b/Puppeteer/Chrome-Recorder/order-a-coffee.json new file mode 100644 index 0000000..e6e69a4 --- /dev/null +++ b/Puppeteer/Chrome-Recorder/order-a-coffee.json @@ -0,0 +1,159 @@ +{ + "title": "order-a-coffee", + "steps": [ + { + "type": "setViewport", + "width": 380, + "height": 604, + "deviceScaleFactor": 1, + "isMobile": false, + "hasTouch": false, + "isLandscape": false + }, + { + "type": "navigate", + "assertedEvents": [ + { + "type": "navigation", + "url": "https://coffee-cart.netlify.app/", + "title": "Coffee cart" + } + ], + "url": "https://coffee-cart.netlify.app/" + }, + { + "type": "click", + "target": "main", + "selectors": [ + [ + "aria/Espresso" + ], + [ + "[data-test=Espresso]" + ] + ], + "offsetX": 168.40187072753906, + "offsetY": 180.0280532836914 + }, + { + "type": "click", + "target": "main", + "selectors": [ + [ + "aria/Cappucino" + ], + [ + "[data-test=Cappucino]" + ] + ], + "offsetX": 168.40187072753906, + "offsetY": 92.24679565429688 + }, + { + "type": "click", + "target": "main", + "selectors": [ + [ + "aria/Proceed to checkout" + ], + [ + "[data-test=checkout]" + ] + ], + "offsetX": 89.1875, + "offsetY": 22.796875 + }, + { + "type": "click", + "target": "main", + "selectors": [ + [ + "aria/Name" + ], + [ + "#name" + ] + ], + "offsetX": 115.015625, + "offsetY": 26.2109375 + }, + { + "type": "change", + "target": "main", + "selectors": [ + [ + "aria/Name" + ], + [ + "#name" + ] + ], + "value": "jane" + }, + { + "type": "keyDown", + "target": "main", + "key": "Tab" + }, + { + "type": "keyUp", + "target": "main", + "key": "Tab" + }, + { + "type": "change", + "target": "main", + "selectors": [ + [ + "aria/Email" + ], + [ + "#email" + ] + ], + "value": "jane@doe.com" + }, + { + "type": "click", + "target": "main", + "selectors": [ + [ + "aria/Promotion message" + ], + [ + "#promotion-label" + ] + ], + "offsetX": 157, + "offsetY": 26.8203125 + }, + { + "type": "click", + "target": "main", + "selectors": [ + [ + "aria/Submit" + ], + [ + "#submit-payment" + ] + ], + "offsetX": 63.3515625, + "offsetY": 27.4296875 + }, + { + "type": "click", + "target": "main", + "selectors": [ + [ + "aria/Thanks for your purchase. Please check your email for payment." + ], + [ + "#app > div.snackbar.success" + ] + ], + "offsetX": 300, + "offsetY": 32.1875 + } + ] +} \ No newline at end of file diff --git a/Puppeteer/Chrome-Recorder/package.json b/Puppeteer/Chrome-Recorder/package.json new file mode 100644 index 0000000..b9a2b63 --- /dev/null +++ b/Puppeteer/Chrome-Recorder/package.json @@ -0,0 +1,20 @@ +{ + "name": "applitools-example", + "version": "1.0.0", + "description": "", + "main": "main.mjs", + "dependencies": { + "dotenv": "^16.0.0" + }, + "devDependencies": { + "@applitools/eyes-puppeteer": "^1.9.0", + "@puppeteer/replay": "^0.1.3", + "puppeteer": "^14.1.2" + }, + "scripts": { + "start": "node main.mjs", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/Puppeteer/Classic/.gitignore b/Puppeteer/Classic/.gitignore new file mode 100644 index 0000000..25c8fdb --- /dev/null +++ b/Puppeteer/Classic/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json \ No newline at end of file diff --git a/Puppeteer/Classic/README.md b/Puppeteer/Classic/README.md new file mode 100644 index 0000000..dc028e2 --- /dev/null +++ b/Puppeteer/Classic/README.md @@ -0,0 +1,85 @@ +# Applitools Tutorial - Puppeteer + +Get started with Applitools Eyes visual testing with these examples of using the [Puppeteer](https://pptr.dev/) and the [Eyes Puppeteer SDK](https://www.npmjs.com/package/@applitools/eyes-puppeteer). + +- [Quick Start](#%EF%B8%8F-quick-start) +- [Running Tests Locally](#-running-the-tests-locally) +- [What's Inside](#-whats-inside) +- [Add Applitools Eyes to your Puppeteer project](#-add-applitools-eyes-to-your-puppeteer-project) + +## ⚡️ Quick Start + +Run your first test in just a few minutes by cloning this repository! + +1. Click the **Use this template** button at the top of the repository or [click here](https://github.com/applitools/tutorial-puppeteer/generate) to create a new repository +2. Add your Applitools API Key as a [repository Secret](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository) under the Settings tab called `APPLITOOLS_API_KEY` +3. Navigate to the Actions tab, find and select the CI workflow, and select Run Workflow + +And that's it! Your tests should now be available to review in the Applitools dashboard. + +## 🚀 Running the Tests Locally + +### Installing Dependencies +``` +npm install +``` + +### Set Applitools API Key + +Before running your test, you need to make your API key available to the environment. You can do this by either prepending your test command or exporting it in your terminal session. + +To prepend, run you a command like `npm test` as: + +``` +APPLITOOLS_API_KEY="[Your Key]" npm test +``` + +To export your API key on a Mac, run: + +``` +export APPLITOOLS_API_KEY="[Your Key]" +``` + +To export your API key on Windows, run: + +``` +set APPLITOOLS_API_KEY="[Your Key]" +``` + +### Running Tests +``` +npm test +``` + +## 🧐 What's inside? + +### Test Examples + +This repository includes a variety of examples on how you can use Applitools Eyes with your Puppeteer project. + +- Basic: running the Eyes SDK without a manually configured runner +- Classic: running the Eyes SDK with the Classic runner (locally) +- Ultrafast: running the Eyes SDK with the Applitools Ultrafast Grid (cloud) + +### GitHub Action Workflows + +Also included are two separate GitHub Actions. + +- tests.yml: runs the suite of tests whenever changes are pushed to the main branch or whenever a pull request is targeted to the main branch +- updates.yml: runs daily via cron schedule updating all project dependencies + +Note: updates.yml is intended to be a tool for Applitools to maintain this tutorial with its limited depedencies and scope. It's not recommended to automatically upgrade all dependencies without process and proper review. + +## 👀 Add Applitools Eyes to your Puppeteer project + +Learn more about how to install and integrate the Eyes SDK with our [Puppeteer tutorial](https://applitools.com/tutorials/puppeteer.html)! + + + +## 🧰 More Information + +Learn more about Applitools [Eyes](https://info.applitools.com/ucY77) and the [Ultrafast Test Cloud](https://info.applitools.com/ucY78) at [applitools.com](https://info.applitools.com/ucY76). + +More about the Eyes Puppeteer SDK: +* https://www.npmjs.com/package/@applitools/eyes-puppeteer +* https://applitools.com/docs/api/eyes-sdk/index-gen/classindex-puppeteer-javascript.html diff --git a/Puppeteer/Classic/package.json b/Puppeteer/Classic/package.json new file mode 100644 index 0000000..d830839 --- /dev/null +++ b/Puppeteer/Classic/package.json @@ -0,0 +1,24 @@ +{ + "name": "puppeteer-eyes-classic", + "version": "1.0.0", + "description": "", + "homepage": "https://applitools.com", + "author": { + "name": "Applitools Team", + "email": "team@applitools.com" + }, + "keywords": [], + "license": "ISC", + "main": "index.js", + "scripts": { + "test": "mocha --no-timeouts 'test/example-classic.test.js'" + }, + "engines": { + "node": ">=8.9.0" + }, + "devDependencies": { + "@applitools/eyes-puppeteer": "^1.9.0", + "mocha": "10.0.0", + "puppeteer": "14.1.2" + } +} diff --git a/Puppeteer/Classic/test/example-classic.test.js b/Puppeteer/Classic/test/example-classic.test.js new file mode 100644 index 0000000..aceb196 --- /dev/null +++ b/Puppeteer/Classic/test/example-classic.test.js @@ -0,0 +1,67 @@ +'use strict'; + +const { Eyes, ClassicRunner, Target, RectangleSize, Configuration, BatchInfo} = require('@applitools/eyes-puppeteer'); +const puppeteer = require('puppeteer') + +describe('Demo App - ClassicRunner - Puppeteer', function () { + let runner, eyes, browser, page; + + beforeEach(async () => { + // Initialize the puppeteer browser + browser = await puppeteer.launch({ + headless: process.env.CI || false + }); + + page = await browser.newPage(); + + // Initialize the Runner for your test. + runner = new ClassicRunner(); + + // Initialize the eyes SDK (IMPORTANT: make sure your API key is set in the APPLITOOLS_API_KEY env variable). + eyes = new Eyes(runner); + + // Initialize the eyes configuration. + const conf = new Configuration() + + // set new batch + conf.setBatch(new BatchInfo('Demo Batch - Puppeteer')); + + // set the configuration to eyes + eyes.setConfiguration(conf) + }); + + it('Smoke Test', async () => { + // Start the test by setting AUT's name, test name and viewport size (width X height) + await eyes.open(page, 'Demo App - Puppeteer - Basic', 'Smoke Test', new RectangleSize(800, 600)); + + // Navigate the browser to the 'ACME' demo app. + await page.goto('https://demo.applitools.com'); + + // To see visual bugs after the first run, use the commented line below instead. + // await page.goto('https://demo.applitools.com/index_v2.html'); + + // Visual checkpoint #1 - Check the login page. + await eyes.check('Login Window', Target.window().fully()); + + // This will create a test with two test steps. + await page.click('#log-in'); + + // Visual checkpoint #2 - Check the app page. + await eyes.check('App Window', Target.window().fully()); + + // End the test. + await eyes.close(); + }); + + afterEach(async () => { + // Close the browser + await browser.close() + + // If the test was aborted before eyes.close was called, ends the test as aborted. + await eyes.abortIfNotClosed(); + + // Wait and collect all test results + const allTestResults = await runner.getAllTestResults(false); + console.log(allTestResults); + }); +}); diff --git a/Puppeteer/Images/README.md b/Puppeteer/Images/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/Puppeteer/Local_Mobile_Native/README.md b/Puppeteer/Local_Mobile_Native/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/Puppeteer/Local_Mobile_Web/README.md b/Puppeteer/Local_Mobile_Web/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/Puppeteer/Local_Web/README.md b/Puppeteer/Local_Web/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/Puppeteer/Remote_Native/README.md b/Puppeteer/Remote_Native/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/Puppeteer/Remote_Web/README.md b/Puppeteer/Remote_Web/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/Puppeteer/UFG/.gitignore b/Puppeteer/UFG/.gitignore new file mode 100644 index 0000000..25c8fdb --- /dev/null +++ b/Puppeteer/UFG/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json \ No newline at end of file diff --git a/Puppeteer/UFG/README.md b/Puppeteer/UFG/README.md index e69de29..dc028e2 100644 --- a/Puppeteer/UFG/README.md +++ b/Puppeteer/UFG/README.md @@ -0,0 +1,85 @@ +# Applitools Tutorial - Puppeteer + +Get started with Applitools Eyes visual testing with these examples of using the [Puppeteer](https://pptr.dev/) and the [Eyes Puppeteer SDK](https://www.npmjs.com/package/@applitools/eyes-puppeteer). + +- [Quick Start](#%EF%B8%8F-quick-start) +- [Running Tests Locally](#-running-the-tests-locally) +- [What's Inside](#-whats-inside) +- [Add Applitools Eyes to your Puppeteer project](#-add-applitools-eyes-to-your-puppeteer-project) + +## ⚡️ Quick Start + +Run your first test in just a few minutes by cloning this repository! + +1. Click the **Use this template** button at the top of the repository or [click here](https://github.com/applitools/tutorial-puppeteer/generate) to create a new repository +2. Add your Applitools API Key as a [repository Secret](https://docs.github.com/en/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository) under the Settings tab called `APPLITOOLS_API_KEY` +3. Navigate to the Actions tab, find and select the CI workflow, and select Run Workflow + +And that's it! Your tests should now be available to review in the Applitools dashboard. + +## 🚀 Running the Tests Locally + +### Installing Dependencies +``` +npm install +``` + +### Set Applitools API Key + +Before running your test, you need to make your API key available to the environment. You can do this by either prepending your test command or exporting it in your terminal session. + +To prepend, run you a command like `npm test` as: + +``` +APPLITOOLS_API_KEY="[Your Key]" npm test +``` + +To export your API key on a Mac, run: + +``` +export APPLITOOLS_API_KEY="[Your Key]" +``` + +To export your API key on Windows, run: + +``` +set APPLITOOLS_API_KEY="[Your Key]" +``` + +### Running Tests +``` +npm test +``` + +## 🧐 What's inside? + +### Test Examples + +This repository includes a variety of examples on how you can use Applitools Eyes with your Puppeteer project. + +- Basic: running the Eyes SDK without a manually configured runner +- Classic: running the Eyes SDK with the Classic runner (locally) +- Ultrafast: running the Eyes SDK with the Applitools Ultrafast Grid (cloud) + +### GitHub Action Workflows + +Also included are two separate GitHub Actions. + +- tests.yml: runs the suite of tests whenever changes are pushed to the main branch or whenever a pull request is targeted to the main branch +- updates.yml: runs daily via cron schedule updating all project dependencies + +Note: updates.yml is intended to be a tool for Applitools to maintain this tutorial with its limited depedencies and scope. It's not recommended to automatically upgrade all dependencies without process and proper review. + +## 👀 Add Applitools Eyes to your Puppeteer project + +Learn more about how to install and integrate the Eyes SDK with our [Puppeteer tutorial](https://applitools.com/tutorials/puppeteer.html)! + + + +## 🧰 More Information + +Learn more about Applitools [Eyes](https://info.applitools.com/ucY77) and the [Ultrafast Test Cloud](https://info.applitools.com/ucY78) at [applitools.com](https://info.applitools.com/ucY76). + +More about the Eyes Puppeteer SDK: +* https://www.npmjs.com/package/@applitools/eyes-puppeteer +* https://applitools.com/docs/api/eyes-sdk/index-gen/classindex-puppeteer-javascript.html diff --git a/Puppeteer/UFG/package.json b/Puppeteer/UFG/package.json new file mode 100644 index 0000000..cc91d16 --- /dev/null +++ b/Puppeteer/UFG/package.json @@ -0,0 +1,24 @@ +{ + "name": "puppeteer-eyes-classic", + "version": "1.0.0", + "description": "", + "homepage": "https://applitools.com", + "author": { + "name": "Applitools Team", + "email": "team@applitools.com" + }, + "keywords": [], + "license": "ISC", + "main": "index.js", + "scripts": { + "test": "mocha --no-timeouts 'test/example-ultrafast.test.js'" + }, + "engines": { + "node": ">=8.9.0" + }, + "devDependencies": { + "@applitools/eyes-puppeteer": "^1.9.0", + "mocha": "10.0.0", + "puppeteer": "14.1.2" + } +} diff --git a/Puppeteer/UFG/test/example-ultrafast.test.js b/Puppeteer/UFG/test/example-ultrafast.test.js new file mode 100644 index 0000000..25e2efd --- /dev/null +++ b/Puppeteer/UFG/test/example-ultrafast.test.js @@ -0,0 +1,97 @@ +'use strict'; + +const { + VisualGridRunner, + RunnerOptions, + Eyes, + Target, + Configuration, + RectangleSize, + BatchInfo, + BrowserType, + DeviceName, + ScreenOrientation +} = require('@applitools/eyes-puppeteer'); +const puppeteer = require('puppeteer') + +let eyes; + +describe('Demo App - Ultrafast Grid - Puppeteer', function () { + let runner, browser, page + + beforeEach(async () => { + // Initialize the puppeteer browser + browser = await puppeteer.launch({ + headless: process.env.CI || false + }); + + page = await browser.newPage(); + + // Create a runner with concurrency of 5 + const runnerOptions = new RunnerOptions().testConcurrency(5) + runner = new VisualGridRunner(runnerOptions); + + // Create Eyes object with the runner, meaning it'll be a Visual Grid eyes. + eyes = new Eyes(runner); + + // Initialize the eyes configuration + const configuration = new Configuration(); + + // create a new batch info instance and set it to the configuration + configuration.setBatch(new BatchInfo('Ultrafast Batch - Puppeteer')) + + // Add browsers with different viewports + configuration.addBrowser(800, 600, BrowserType.CHROME); + configuration.addBrowser(700, 500, BrowserType.FIREFOX); + configuration.addBrowser(1600, 1200, BrowserType.IE_11); + configuration.addBrowser(1024, 768, BrowserType.EDGE_CHROMIUM); + configuration.addBrowser(800, 600, BrowserType.SAFARI); + + // Add mobile emulation devices in Portrait mode + configuration.addDeviceEmulation(DeviceName.iPhone_X, ScreenOrientation.PORTRAIT); + configuration.addDeviceEmulation(DeviceName.Pixel_2, ScreenOrientation.PORTRAIT); + + // Set the configuration to eyes + eyes.setConfiguration(configuration); + }); + + + it('ultraFastTest', async () => { + + // Navigate to the url we want to test + // ⭐️ Note to see visual bugs, run the test using the above URL for the 1st run. + // but then change the above URL to https://demo.applitools.com/index_v2.html + // (for the 2nd run) + await page.goto('https://demo.applitools.com'); + + // Call Open on eyes to initialize a test session + await eyes.open(page, 'Demo App - Puppeteer - Ultrafast Grid', 'Smoke Test', new RectangleSize(800, 600)); + + // check the login page with fluent api, see more info here + // https://applitools.com/docs/topics/sdk/the-eyes-sdk-check-fluent-api.html + await eyes.check('Login Window', Target.window().fully()); + + // Click the "Log in" button. + await page.click("#log-in"); + + // Check the app page + await eyes.check('App Window', Target.window().fully()); + + // Call Close on eyes to let the server know it should display the results + await eyes.close(); + }); + + afterEach(async () => { + // Close the browser + await browser.close() + + // If the test was aborted before eyes.close was called, ends the test as aborted. + await eyes.abortIfNotClosed(); + + // we pass false to this method to suppress the exception that is thrown if we + // find visual differences + const results = await runner.getAllTestResults(false); + console.log(results); + }); + +}); \ No newline at end of file