44 |
├── src ├── files │ ├── test_data.json │ ├── dummy.pdf │ └── dummy.zip ├── js │ ├── footer.js │ ├── loans.js │ ├── savings.js │ ├── index.js │ └── jquery-3.6.0.min.js ├── css │ └── custom.css ├── index.html ├── savings.html └── loans.html ├── .gitignore ├── playwright.config.ts ├── server.js ├── tests └── m2-setup │ └── _0setup_verification.js ├── package.json └── README.md /src/files/test_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "Key 1": "Route FulFill Demo", 3 | "Key 2": "Key 2" 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js 2 | node_modules/ 3 | test-results/ 4 | 5 | .idea/ 6 | playwright-report/ -------------------------------------------------------------------------------- /src/files/dummy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejs-ps/playwright-nodejs-starter/HEAD/src/files/dummy.pdf -------------------------------------------------------------------------------- /src/files/dummy.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejs-ps/playwright-nodejs-starter/HEAD/src/files/dummy.zip -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@playwright/test'; 2 | 3 | export default defineConfig({ 4 | testDir: './tests', 5 | reporter: 'html' 6 | }); 7 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | 4 | app.use(express.static(__dirname + '/src')); 5 | 6 | app.listen('3000'); 7 | console.log('working on 3000'); -------------------------------------------------------------------------------- /src/js/footer.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 'use strict' 3 | 4 | setTimeout(() => { 5 | document.getElementById('location').innerText = `You are visiting us from New York`; 6 | }, "2000"); 7 | 8 | })() 9 | -------------------------------------------------------------------------------- /tests/m2-setup/_0setup_verification.js: -------------------------------------------------------------------------------- 1 | const { chromium } = require('@playwright/test'); 2 | 3 | (async function firstScript() { 4 | const browser = await chromium.launch(); 5 | await browser.close(); 6 | console.log("we reached this line"); 7 | })(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playwright-javascript", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@playwright/test": "^1.40.0", 15 | "@types/node": "^20.9.0" 16 | }, 17 | "dependencies": { 18 | "express": "^4.18.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Demo code for my Pluralsight course: Playwright in Node.js Fundamentals 2 | 3 | Starting a new end-to-end test automation project? My course will teach you how to test web applications using the open-source tool Playwright with Node.js. Try it and never look back! 4 | 5 | > [!IMPORTANT] 6 | > This is a "starter" repo, meaning it contains only the minimal setup to enable learners to code along with the video course. 7 | 8 | You can find all my courses at: https://www.pluralsight.com/authors/andrejs-doronins 9 | -------------------------------------------------------------------------------- /src/js/loans.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 'use strict' 3 | 4 | const applyBtn = document.getElementById('apply'); 5 | 6 | applyBtn.addEventListener('click', () => { 7 | const msg = document.getElementById('message'); 8 | msg.classList.add("alert"); 9 | msg.innerText = 'Thank you for applying' 10 | 11 | }) 12 | 13 | let input = document.getElementById('borrow'); 14 | let period = document.getElementById('period'); 15 | let result = document.getElementById('result'); 16 | 17 | input.addEventListener('input', event => { 18 | setTimeout(displayResult, 3000); 19 | }); 20 | 21 | period.addEventListener('change', event => { 22 | setTimeout(displayResult, 3000); 23 | }); 24 | 25 | function displayResult() { 26 | let sum = input.value; 27 | if (sum < 1) { return; } 28 | result.removeAttribute("style"); 29 | result.innerText = `You will pays us back ${sum * 2}`; 30 | } 31 | 32 | })() 33 | -------------------------------------------------------------------------------- /src/css/custom.css: -------------------------------------------------------------------------------- 1 | main > .container { 2 | padding: 60px 15px 0; 3 | } 4 | .bd-placeholder-img { 5 | font-size: 1.125rem; 6 | text-anchor: middle; 7 | -webkit-user-select: none; 8 | -moz-user-select: none; 9 | user-select: none; 10 | } 11 | 12 | @media (min-width: 768px) { 13 | .bd-placeholder-img-lg { 14 | font-size: 3.5rem; 15 | } 16 | } 17 | 18 | .b-example-divider { 19 | height: 3rem; 20 | background-color: rgba(0, 0, 0, .1); 21 | border: solid rgba(0, 0, 0, .15); 22 | border-width: 1px 0; 23 | box-shadow: inset 0 .5em 1.5em rgba(0, 0, 0, .1), inset 0 .125em .5em rgba(0, 0, 0, .15); 24 | } 25 | 26 | .b-example-vr { 27 | flex-shrink: 0; 28 | width: 1.5rem; 29 | height: 100vh; 30 | } 31 | 32 | .bi { 33 | vertical-align: -.125em; 34 | fill: currentColor; 35 | } 36 | 37 | .nav-scroller { 38 | position: relative; 39 | z-index: 2; 40 | height: 2.75rem; 41 | overflow-y: hidden; 42 | } 43 | 44 | .nav-scroller .nav { 45 | display: flex; 46 | flex-wrap: nowrap; 47 | padding-bottom: 1rem; 48 | margin-top: -1px; 49 | overflow-x: auto; 50 | text-align: center; 51 | white-space: nowrap; 52 | -webkit-overflow-scrolling: touch; 53 | } -------------------------------------------------------------------------------- /src/js/savings.js: -------------------------------------------------------------------------------- 1 | 2 | (() => { 3 | 'use strict' 4 | const periodYields = new Map([ 5 | [0.5, 0.04], 6 | [1, 0.05], 7 | [2, 0.06], 8 | ]); 9 | 10 | let input = document.getElementById('deposit'); 11 | let period = document.getElementById('period'); 12 | let result = document.getElementById('result'); 13 | 14 | input.addEventListener('input', event => { 15 | displayReturn(); 16 | }); 17 | 18 | period.addEventListener('change', event => { 19 | displayReturn(); 20 | }); 21 | 22 | function displayReturn() { 23 | let sum = input.value; 24 | if (sum < 1) { return; } 25 | let selected = period.options[period.selectedIndex].value; 26 | let periodInYears = convertPeriod(selected); 27 | let expectedReturn = calculateReturn(sum, periodInYears); 28 | let roundedReturn = parseFloat(expectedReturn).toFixed(2); 29 | result.classList.add("alert"); 30 | result.innerText = `After ${selected} you will earn $${roundedReturn} on your deposit`; 31 | } 32 | 33 | function convertPeriod(option) { 34 | let periodKeys = Array.from(periodYields.keys()); 35 | switch (option) { 36 | case '6 Months': 37 | return periodKeys[0]; 38 | case '1 Year': 39 | return periodKeys[1]; 40 | case '2 Years': 41 | return periodKeys[2]; 42 | } 43 | } 44 | 45 | function calculateReturn(sum, periodInYears) { 46 | let yearlyYield = periodYields.get(periodInYears); 47 | return sum * periodInYears * yearlyYield; 48 | } 49 | 50 | 51 | })() 52 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 'use strict' 3 | const forms = document.querySelectorAll('.needs-validation'); 4 | 5 | // Prevent submission 6 | Array.from(forms).forEach(form => { 7 | form.addEventListener('submit', event => { 8 | if (!form.checkValidity()) { 9 | event.preventDefault() 10 | event.stopPropagation() 11 | } 12 | }, false); 13 | }) 14 | 15 | const registerBtn = document.getElementById('register'); 16 | registerBtn.addEventListener('click', () => { 17 | Array.from(forms).forEach(form => { 18 | form.classList.add('was-validated') 19 | }) 20 | printToConsole(); 21 | }) 22 | 23 | function printToConsole() { 24 | console.log("Application submitted"); 25 | console.warn("Connection is slow"); 26 | console.error("Something went wrong"); 27 | 28 | throw new Error("Deliberate error"); 29 | } 30 | 31 | const inputs = document.getElementsByClassName('form-control'); 32 | const clearBtn = document.getElementById('clear'); 33 | clearBtn.addEventListener('click', () => { 34 | if (confirm('This will clear all inputs. Continue?')) { 35 | Array.from(inputs).forEach(input => input.value = ''); 36 | } 37 | 38 | }); 39 | 40 | const saveBtn = document.getElementById('save'); 41 | saveBtn.addEventListener('click', () => { 42 | Array.from(inputs).forEach(input => { 43 | localStorage.setItem(input.getAttribute('id'), input.value); 44 | }); 45 | }); 46 | 47 | document.addEventListener("DOMContentLoaded", function () { 48 | 49 | Array.from(inputs).forEach(input => { 50 | let storedValue = localStorage.getItem(input.getAttribute('id')); 51 | if (storedValue) { 52 | input.value = storedValue; 53 | } 54 | }); 55 | 56 | }); 57 | })() 58 | 59 | 60 | 61 | $(document).ready(function () { 62 | 63 | $('#heard-about').click(function () { 64 | if ($("#textarea").attr("disabled")) { 65 | $('#textarea').removeAttr("disabled"); 66 | } else { 67 | $('#textarea').prop('disabled', true); 68 | $('#textarea').val(""); 69 | } 70 | }); 71 | }); -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eu vestibulum dui. Proin aliquet tortor non aliquet congue. Nunc in blandit eros, et pretium diam. Nulla facilisi. Nullam ligula nulla, laoreet in suscipit sit amet, finibus eget odio. Vivamus varius malesuada magna in interdum. Fusce lacinia condimentum sem, ac feugiat velit. Morbi vulputate eros sit amet fringilla rhoncus. Cras sed pulvinar dolor. 79 | 80 | Sed placerat lectus et mollis fermentum. Aliquam suscipit rutrum neque, a tristique erat fermentum nec. Cras a aliquet nisl. Cras tincidunt laoreet massa, eget dictum risus pellentesque eu. Cras eget laoreet risus. Aenean sed est maximus, vestibulum mauris suscipit, hendrerit quam. In hendrerit neque eu enim tristique, vel faucibus dolor dapibus. 81 | 82 | Nunc eu leo aliquam, feugiat diam vitae, auctor enim. Donec interdum odio vitae leo bibendum, id scelerisque mi dignissim. Integer condimentum dictum risus luctus posuere. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse vel vulputate risus. Pellentesque hendrerit non lacus vitae semper. Nulla semper molestie magna, non tempus turpis luctus non. Sed odio dolor, efficitur at efficitur ut, finibus at libero. Pellentesque vitae risus a risus vestibulum mollis. Nam ullamcorper turpis id risus tincidunt malesuada. Cras aliquet leo feugiat justo varius, efficitur facilisis velit viverra. Cras augue est, suscipit id nisl sed, faucibus aliquam elit. Aliquam nec aliquet neque. Maecenas euismod mi fermentum vehicula suscipit. 83 | 84 | Aenean blandit, sapien in aliquam ornare, eros nisi pellentesque metus, et laoreet mi dui vel ex. Donec ullamcorper leo mi, eget ultricies nisl ultrices sed. Morbi auctor sapien a elit vestibulum, sit amet ullamcorper justo viverra. Morbi faucibus mattis malesuada. Praesent bibendum malesuada turpis, a maximus risus egestas ac. Vestibulum laoreet nisl orci, ac rutrum sapien sollicitudin a. Integer commodo fringilla rhoncus. Aenean arcu sem, luctus ut porttitor in, bibendum sit amet magna. Aenean et ultrices sapien. Praesent sit amet pellentesque ligula. 85 | 86 | Morbi gravida, tortor sit amet suscipit vestibulum, nulla lectus sagittis orci, et dignissim ipsum urna et augue. Vivamus et quam non magna fringilla aliquam id eu lorem. Aliquam aliquam blandit dui, ac porta ante rhoncus et. In at vulputate ligula, in blandit augue. Pellentesque faucibus elit vitae odio lobortis, ac bibendum nibh placerat. Phasellus cursus nisl porta nisi tempor aliquet. Etiam accumsan ut mi malesuada vestibulum. Maecenas dapibus mauris nec nunc laoreet iaculis. Nunc ut neque feugiat, ultricies purus id, placerat purus. Nullam sollicitudin ante et consequat lobortis. Praesent lacinia, velit nec malesuada varius, nibh turpis porta velit, ut imperdiet nibh tellus egestas mauris. Nulla efficitur imperdiet fringilla. 87 |
88 |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eu vestibulum dui. Proin aliquet tortor non aliquet congue. Nunc in blandit eros, et pretium diam. Nulla facilisi. Nullam ligula nulla, laoreet in suscipit sit amet, finibus eget odio. Vivamus varius malesuada magna in interdum. Fusce lacinia condimentum sem, ac feugiat velit. Morbi vulputate eros sit amet fringilla rhoncus. Cras sed pulvinar dolor. 89 | 90 | Sed placerat lectus et mollis fermentum. Aliquam suscipit rutrum neque, a tristique erat fermentum nec. Cras a aliquet nisl. Cras tincidunt laoreet massa, eget dictum risus pellentesque eu. Cras eget laoreet risus. Aenean sed est maximus, vestibulum mauris suscipit, hendrerit quam. In hendrerit neque eu enim tristique, vel faucibus dolor dapibus. 91 | 92 | Nunc eu leo aliquam, feugiat diam vitae, auctor enim. Donec interdum odio vitae leo bibendum, id scelerisque mi dignissim. Integer condimentum dictum risus luctus posuere. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse vel vulputate risus. Pellentesque hendrerit non lacus vitae semper. Nulla semper molestie magna, non tempus turpis luctus non. Sed odio dolor, efficitur at efficitur ut, finibus at libero. Pellentesque vitae risus a risus vestibulum mollis. Nam ullamcorper turpis id risus tincidunt malesuada. Cras aliquet leo feugiat justo varius, efficitur facilisis velit viverra. Cras augue est, suscipit id nisl sed, faucibus aliquam elit. Aliquam nec aliquet neque. Maecenas euismod mi fermentum vehicula suscipit. 93 | 94 | Aenean blandit, sapien in aliquam ornare, eros nisi pellentesque metus, et laoreet mi dui vel ex. Donec ullamcorper leo mi, eget ultricies nisl ultrices sed. Morbi auctor sapien a elit vestibulum, sit amet ullamcorper justo viverra. Morbi faucibus mattis malesuada. Praesent bibendum malesuada turpis, a maximus risus egestas ac. Vestibulum laoreet nisl orci, ac rutrum sapien sollicitudin a. Integer commodo fringilla rhoncus. Aenean arcu sem, luctus ut porttitor in, bibendum sit amet magna. Aenean et ultrices sapien. Praesent sit amet pellentesque ligula. 95 | 96 | Morbi gravida, tortor sit amet suscipit vestibulum, nulla lectus sagittis orci, et dignissim ipsum urna et augue. Vivamus et quam non magna fringilla aliquam id eu lorem. Aliquam aliquam blandit dui, ac porta ante rhoncus et. In at vulputate ligula, in blandit augue. Pellentesque faucibus elit vitae odio lobortis, ac bibendum nibh placerat. Phasellus cursus nisl porta nisi tempor aliquet. Etiam accumsan ut mi malesuada vestibulum. Maecenas dapibus mauris nec nunc laoreet iaculis. Nunc ut neque feugiat, ultricies purus id, placerat purus. Nullam sollicitudin ante et consequat lobortis. Praesent lacinia, velit nec malesuada varius, nibh turpis porta velit, ut imperdiet nibh tellus egestas mauris. Nulla efficitur imperdiet fringilla. 97 |
98 | 99 |