SlideShare a Scribd company logo
1 of 85
Download to read offline
possibly
Puppeteer can
automate that!
with the help of minions
Önder Ceylan
@onderceylan
Sharing knowledge on #javascript, #typescript, #angular, #ionic and #pwa
JS Squad Lead @LINKIT
Speaker, Organiser @ITNEXT
Speaker, Organiser @GDG NL
Chrome
Chrome
canarychromiumchrome
Headless Chrome
Headless Chrome
chrome —-headless —-remote-debugging-port=9222
Puppeteer
Puppeteer
npm i puppeteer
“Most things that you can do
manually in the browser can be
done using Puppeteer! ”
Examples to get you started
• Generate screenshots and PDFs of pages
• Crawl a SPA and generate pre-rendered
content
• Automate form submission, UI testing,
keyboard input, etc
• Capture a timeline trace of your site to
help diagnose performance issues
puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  await page.goto('https://www.linkit.nl');
  await page.screenshot({path: 'screenshot.png'});
  await browser.close();
});
pptr.dev
try-puppeteer.appspot.com
github.com/transitive-bullshit/awesome-puppeteer
Puppeteer Recorder Extension
DEMOS
WhatsApp messages every day!
const browser = await puppeteer.launch(({
userDataDir: ".tmp",
}));
const page = await browser.newPage();
await page.goto('https://web.whatsapp.com/', {waitUntil: 'networkidle2'});
await page.waitForSelector('#side input[type=text]');
await page.type('#side input[type=text]', groupName);
await page.waitForSelector(`#pane-side span[title=“${groupName}"]`,
{visible: true});
await page.click(`span[title="${groupName}"`);
await page.waitForSelector('footer .copyable-text', {visible: true});
await page.type('footer .copyable-text', getTodaysMessage());
await page.keyboard.press('Enter');
await browser.close();
WhatsApp messages every day!
Emulating color scheme NEW
in v2
await page.goto('https://googlechromelabs.github.io/dark-mode-toggle/demo/index.html');
await page.emulateMediaFeatures([{ name: 'prefers-color-scheme', value: 'light' }]);
await page.screenshot({ path: 'light.jpg', type: 'jpeg', omitBackground: true });
await page.emulateMediaFeatures([{ name: 'prefers-color-scheme', value: 'dark' }]);
await page.screenshot({ path: 'dark.jpg', type: 'jpeg', omitBackground: true });
Emulating color scheme NEW
in v2
Side by side page load
const devices = require('puppeteer/DeviceDescriptors');
const nexus5X = devices['Nexus 5X'];
const browser = await puppeteer.launch({
headless: false,
args: [
`--window-size=${DEFAULT_VIEWPORT.width},${DEFAULT_VIEWPORT.height}`,
CENTER_WINDOWS_ON_SCREEN ? `--window-position=${x},${y}` : `--window-position=${dx},0`,
],
});
const page = await browser.newPage();
await page.emulate(nexus5X);
const session = await page.target().createCDPSession();
// Emulate "Slow 3G" according to WebPageTest
await session.send('Network.emulateNetworkConditions', {
offline: false,
latency: 400,
downloadThroughput: Math.floor(400 * 1024 / 8), // 400 Kbps
uploadThroughput: Math.floor(400 * 1024 / 8) // 400 Kbps
});
await session.send('Emulation.setCPUThrottlingRate', {rate: 4});
Side by side page load
Accessibility test
await page.goto(‘https://www.linkit.nl');
await page.addScriptTag(
{ url: 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/3.3.2/axe.min.js' }
);
const results = await page.evaluate(() => axe.run(document));
expect(results.violations.length).toBe(0);
Accessibility test
Code coverage test
const pti = require('puppeteer-to-istanbul');
const page = await browser.newPage();
await page.coverage.startJSCoverage();
await page.goto('https://www.linkit.nl/');
const jsCoverage = await page.coverage.stopJSCoverage();
pti.write(jsCoverage);
await page.close();
Code coverage test
Chrome DevTools
throttle network
track memory usage
emulate devices
run audits
github.com/ChromeDevTools/devtools-frontend
Protocol Monitor
chrome://flags/#enable-devtools-experiments
chrome —-remote-debugging-port=9222
Chrome DevTools
Protocol
127.0.0.1:9222/json/version
chromedevtools.github.io/devtools-protocol
Using DevTools
Protocol with PPTR
puppeteer.launch().then(async browser => {
  const page = await browser.newPage();
  await page.goto('https://www.linkit.nl');
const session = await page.target().createCDPSession();
  session.on('Animation.animationCreated', () => {
    console.log('Animation created!');
  });
  await session.send('Animation.enable');
  await session.send('Animation.setPlaybackRate', {
    playbackRate: 10,
  });
  await browser.close();
});
Puppeteer Core
npm i puppeteer-core
Puppeteer Core
browser = await puppeteer.connect({
  browserWSEndpoint: 'ws://127.0.0.1:9222/.../...'
});
// or 
browser = await puppeteer.launch({
  executablePath: '/Applications/Google Chrome.app
/Contents/MacOS/Google Chrome'
});
Chrome Launcher
npm i chrome-launcher
const chromeLauncher = require('chrome-launcher');
// Launches a debugging instance of Chrome
function launchChrome(headless=true) {
  return chromeLauncher.launch({
    // port: 9222,
    chromeFlags: [
      '--window-size=412,732',
      '--disable-gpu',
      headless ? '--headless' : ''
    ]
  });
}
launchChrome().then(chrome => {
  console.log(`Chrome debuggable on port: ${chrome.port}`);
  // chrome.kill();
});
const chromeLauncher = require('chrome-launcher');
// Launches a debugging instance of Chrome
function launchChrome(headless=true) {
  return chromeLauncher.launch({
    // port: 9222,
    chromeFlags: [
      '--window-size=412,732',
      '--disable-gpu',
      headless ? '--headless' : ''
    ]
  });
}
launchChrome().then(chrome => {
  console.log(`Chrome debuggable on port: ${chrome.port}`);
  // chrome.kill();
});
chrome instance
Puppeteer Light
npm i puppeteer-core
npm i chrome-launcher
const chromeLauncher = require('chrome-launcher');
const puppeteer = require('puppeteer-core');
const util = require('util');
(async() => {
const URL = 'https://www.linkit.nl';
// Chrome executable launch arguments
const opts = {
  chromeFlags: ['--headless'],
  logLevel: 'info',
  output: 'json'
};
// Launching chrome using chrome-launcher
const chrome = await chromeLauncher.launch(opts);
opts.port = chrome.port;
// Connecting to WS using puppeteer.connect()
const resp = await util.promisify(request)(`http://localhost:${opts.port}/json/version`);
const chromeVersion = JSON.parse(resp.body);
const browser = await puppeteer.connect({
  browserWSEndpoint: chromeVersion.webSocketDebuggerUrl
});
// Cleanup of resources
await browser.disconnect();
await chrome.kill();
})();
const chromeLauncher = require('chrome-launcher');
const puppeteer = require(‘puppeteer-core');
const util = require('util');
(async() => {
const URL = 'https://www.linkit.nl';
// Chrome executable launch arguments
const opts = {
  chromeFlags: ['--headless'],
  logLevel: 'info',
  output: 'json'
};
// Launching chrome using chrome-launcher
const chrome = await chromeLauncher.launch(opts);
opts.port = chrome.port;
// Connecting to WS using puppeteer.connect()
const resp = await util.promisify(request)(`http://localhost:${opts.port}/json/version`);
const chromeVersion = JSON.parse(resp.body);
const browser = await puppeteer.connect({
  browserWSEndpoint: chromeVersion.webSocketDebuggerUrl
});
// Cleanup of resources
await browser.disconnect();
await chrome.kill();
})();
const chromeLauncher = require('chrome-launcher');
const puppeteer = require(‘puppeteer-core');
const util = require('util');
(async() => {
const URL = 'https://www.linkit.nl';
// Chrome executable launch arguments
const opts = {
  chromeFlags: ['--headless'],
  logLevel: 'info',
  output: 'json'
};
// Launching chrome using chrome-launcher
const chrome = await chromeLauncher.launch(opts);
opts.port = chrome.port;
// Connecting to WS using puppeteer.connect()
const resp = await util.promisify(request)(`http://localhost:${opts.port}/json/version`);
const chromeVersion = JSON.parse(resp.body);
const browser = await puppeteer.connect({
  browserWSEndpoint: chromeVersion.webSocketDebuggerUrl
});
// Cleanup of resources
await browser.disconnect();
await chrome.kill();
})();
const chromeLauncher = require('chrome-launcher');
const puppeteer = require(‘puppeteer-core');
const util = require('util');
(async() => {
const URL = 'https://www.linkit.nl';
// Chrome executable launch arguments
const opts = {
  chromeFlags: ['--headless'],
  logLevel: 'info',
  output: 'json'
};
// Launching chrome using chrome-launcher
const chrome = await chromeLauncher.launch(opts);
opts.port = chrome.port;
// Connecting to WS using puppeteer.connect()
const resp = await util.promisify(request)(`http://localhost:${opts.port}/json/version`);
const chromeVersion = JSON.parse(resp.body);
const browser = await puppeteer.connect({
  browserWSEndpoint: chromeVersion.webSocketDebuggerUrl
});
// Cleanup of resources
await browser.disconnect();
await chrome.kill();
})();
const chromeLauncher = require('chrome-launcher');
const puppeteer = require(‘puppeteer-core');
const util = require('util');
(async() => {
const URL = 'https://www.linkit.nl';
// Chrome executable launch arguments
const opts = {
  chromeFlags: ['--headless'],
  logLevel: 'info',
  output: 'json'
};
// Launching chrome using chrome-launcher
const chrome = await chromeLauncher.launch(opts);
opts.port = chrome.port;
// Connecting to WS using puppeteer.connect()
const resp = await util.promisify(request)(`http://localhost:${opts.port}/json/version`);
const chromeVersion = JSON.parse(resp.body);
const browser = await puppeteer.connect({
  browserWSEndpoint: chromeVersion.webSocketDebuggerUrl
});
// Cleanup of resources
await browser.disconnect();
await chrome.kill();
})();
DEMOS
even more
github.com/onderceylan/pwa-asset-generator
PptrGram
await Promise.all(filters.map(async (filter) => {
const page = await browser.newPage();
await page.setContent(`
<style>/* Page styles here */</style>
<figure class="${filter}">
<img src="${getImageBase64Url('./sample.jpg')}">
<span>@onderceylan</span>
</figure>
`, { waitUntil: 'networkidle2' });
await page.addStyleTag({
url: 'https://cdnjs.cloudflare.com/ajax/libs/cssgram/0.1.10/cssgram.min.css'
});
// Get original image dimensions
const { width, height } = await page.evaluate(() => {
return (({naturalWidth: width, naturalHeight: height}) =>
({width, height}))(document.querySelector('img'));
});
await page.setViewport({ width, height });
await page.screenshot({
path: `pptgram/pptrgram-${filter}.jpeg`,
type: 'jpeg',
quality: 70,
fullPage: true,
});
});
PptrGram
PptrFlow
PptrFlow
PptrFlow
await page.goto(`http://localhost:8002/tensorflow.html`);
const result = await page.evaluate(() => {
const img = document.getElementById('img');
// Load the model
return cocoSsd.load().then(model => model.detect(img))
});
console.log(result);
PptrFlow
Monitoring
Visual Regression Testing
const takeScreenshot = async (page, title) => {
if (!fs.existsSync('./.screenshots')) {
fs.mkdirSync('./.screenshots');
}
const filePath = `./.screenshots/${title}.png`;
if (fs.existsSync(filePath)) {
const newFilePath = `./.screenshots/${title}-new.png`;
await page.screenshot({
path: newFilePath,
fullPage: true
});
const result = await new Promise(resolve =>
looksSame(filePath, newFilePath, (err, equal) => resolve(equal)));
fs.unlinkSync(newFilePath);
return result;
} else {
await page.screenshot({
path: filePath,
fullPage: true
});
return true;
}
};
await page.goto('https://www.linkit.nl');
expect(await takeScreenshot(page, 'main-page.1')).toBeTruthy();
Visual Regression Testing
DOM Snapshot Testing
const page = await browser.newPage();
await page.goto('https://www.linkit.nl/');
expect(await page.content()).toMatchSnapshot();
DOM Snapshot Testing
Budgets on perf metrics
Budgets on perf metrics
{ name: 'Timestamp', value: 66567.150449 },
{ name: 'AudioHandlers', value: 0 },
{ name: 'Documents', value: 8 },
{ name: 'Frames', value: 3 },
{ name: 'JSEventListeners', value: 34 },
{ name: 'LayoutObjects', value: 455 },
{ name: 'MediaKeySessions', value: 0 },
{ name: 'MediaKeys', value: 0 },
{ name: 'Nodes', value: 970 },
{ name: 'Resources', value: 74 },
{ name: 'ContextLifecycleStateObservers',
value: 0 },
{ name: 'V8PerContextDatas', value: 4 },
{ name: 'WorkerGlobalScopes', value: 0 },
{ name: 'UACSSResources', value: 0 },
{ name: 'RTCPeerConnections', value: 0 },
{ name: 'ResourceFetchers', value: 8 },
{ name: 'AdSubframes', value: 0 }
{ name: 'DetachedScriptStates', value: 2 },
{ name: 'LayoutCount', value: 13 },
{ name: 'RecalcStyleCount', value: 22 },
{ name: 'LayoutDuration', value: 0.067929 },
{ name: 'RecalcStyleDuration', value: 0.029508 },
{ name: 'ScriptDuration', value: 0.122922 },
{ name: 'V8CompileDuration', value: 0.003031 },
{ name: 'TaskDuration', value: 0.336774 },
{ name: 'TaskOtherDuration', value: 0.116415 },
{ name: 'ThreadTime', value: 0.275266 },
{ name: 'JSHeapUsedSize', value: 7816504 },
{ name: 'JSHeapTotalSize', value: 11096064 },
{ name: 'FirstMeaningfulPaint', value: 66565.452541 },
{ name: 'DomContentLoaded', value: 66565.386449 },
{ name: 'NavigationStart', value: 66564.624457 }
Performance.getMetrics
Budgets on perf metrics
[
{
"path": "/",
"perfMetrics": [
{
"metric": "JSEventListeners",
"budget": 100
},
{
"metric": "Nodes",
"budget": 2000
},
{
"metric": "JSHeapUsedSize",
"budget": 20000000
}
]
}
]
[
{
"path": "/vacatures",
"perfMetrics": [
{
"metric": "Resources",
"budget": 80
}
]
}
]
budget.json
const { getBudgetMetricsOfPage, getMatchedPageMetrics,
getBudgetMetricByPageMetricName } = require(‘./helpers');
const assertMetricsOnUrl = async (siteUrl) => {
const page = await this.browser.newPage();
const protocol = await page.target().createCDPSession();
await protocol.send('Performance.enable');
await page.goto(siteUrl, { waitUntil: 'networkidle0' });
const budgetMetrics = await getBudgetMetricsOfPage(page);
const { metrics: pageMetrics } = await protocol.send('Performance.getMetrics');
const matchedMetrics = getMatchedPageMetrics(pageMetrics, budgetMetrics);
matchedMetrics.forEach(pageMetric => {
expect(pageMetric.value).toBeLessThan(
getBudgetMetricByPageMetricName(budgetMetrics, pageMetric)
);
});
await page.close();
};
Budgets on perf metrics
beforeAll(async () => {
this.browser = await puppeteer.launch();
});
afterAll(async () => {
await this.browser.close();
});
test('asserts budget performance metrics on the main page', async() => {
await assertMetricsOnUrl('https://www.linkit.nl/');
});
test('asserts budget performance metrics on vacatures page', async() => {
await assertMetricsOnUrl('https://www.linkit.nl/vacatures');
});
Budgets on perf metrics
FPS Monitoring
const protocol = await page.target().createCDPSession();
await protocol.send('Overlay.setShowFPSCounter', { show: true });
await page.goto('https://www.linkit.nl');
// Do graphical regressions here by interacting with the page
await protocol.send('Input.synthesizeScrollGesture', {
x: 100,
y: 100,
yDistance: -400,
repeatCount: 3
});
await page.screenshot({
path: 'fps.jpeg',
type: 'jpeg',
clip: {
x:0,
y:0,
width: 370,
height: 370
}
});
FPS Monitoring
Memory leak by Heap
const protocol = await page.target().createCDPSession();
await protocol.send('HeapProfiler.enable');
await protocol.send('HeapProfiler.collectGarbage');
const startMetrics = await page.metrics();
// Do memory regressions here by interacting with the page
await protocol.send('Input.synthesizeScrollGesture', {
x: 100,
y: 100,
yDistance: -400,
repeatCount: 3
});
await protocol.send('HeapProfiler.collectGarbage');
const endMetrics = await page.metrics();
expect(endMetrics.JSHeapUsedSize < startMetrics.JSHeapUsedSize * 1.1)
.toBeTruthy();
Memory leak by Heap
Monitor SSL certificate expiration
const page = await browser.newPage();
page.on('response', (resp) => {
const url = resp.url();
if (url === siteUrl) {
const secDetails = resp.securityDetails();
const now = Math.floor((new Date()).getTime() / 1000);
console.log((Math.floor((secDetails.validTo() - now) / 86400)),
'days to expire');
}
});
await page.goto(siteUrl, { waitUntil: 'networkidle0' });
Monitor SSL certificate expiration
Puppeteer on Cloud
const launchChrome = require('@serverless-chrome/lambda');
const request = require('superagent');
module.exports.getChrome = async () => {
const chrome = await launchChrome();
const response = await request
.get(`${chrome.url}/json/version`)
.set('Content-Type', 'application/json');
const endpoint = response.body.webSocketDebuggerUrl;
return {
endpoint,
instance: chrome,
};
};
PPTR on AWS Lambda via Serverless Framework
const puppeteer = require('puppeteer');
const { getChrome } = require('./chrome-script');
module.exports.hello = async (event) => {
const { url } = event.queryStringParameters;
const chrome = await getChrome();
const browser = await puppeteer.connect({
browserWSEndpoint: chrome.endpoint,
});
const page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle0' });
const content = await page.evaluate(() => document.body.innerHTML);
return {
statusCode: 200,
body: JSON.stringify({
content,
}),
};
};
PPTR on AWS Lambda via Serverless Framework
PaaS
Puppeteer as a Service
browserless.io
checklyhq.com
Main Takeaways
• Chrome can be instrumented with a WS connection over
Chrome DevTools Protocol — CDP
• You might not need to download chromium revision every time
—puppeteer-core + chrome-launcher
• Headless chrome can be executed on servers— cloud, and CI
pipelines
• You can automate anything you do on DevTools, by using raw
protocol of CDP on puppeteer—CDPSession
bit.ly/pptr-demos
Thank you!
bit.ly/pptr-talk@onderceylan

More Related Content

What's hot

Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in KotlinAlexey Soshin
 
Detecting headless browsers
Detecting headless browsersDetecting headless browsers
Detecting headless browsersSergey Shekyan
 
Introduction to gRPC: A general RPC framework that puts mobile and HTTP/2 fir...
Introduction to gRPC: A general RPC framework that puts mobile and HTTP/2 fir...Introduction to gRPC: A general RPC framework that puts mobile and HTTP/2 fir...
Introduction to gRPC: A general RPC framework that puts mobile and HTTP/2 fir...Codemotion
 
Coding with golang
Coding with golangCoding with golang
Coding with golangHannahMoss14
 
REST API Design & Development
REST API Design & DevelopmentREST API Design & Development
REST API Design & DevelopmentAshok Pundit
 
Learning jQuery in 30 minutes
Learning jQuery in 30 minutesLearning jQuery in 30 minutes
Learning jQuery in 30 minutesSimon Willison
 
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드NAVER Engineering
 
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...JassGroup TICS
 
Service workers
Service workersService workers
Service workersjungkees
 
HTTP2 and gRPC
HTTP2 and gRPCHTTP2 and gRPC
HTTP2 and gRPCGuo Jing
 
Long running processes in DDD
Long running processes in DDDLong running processes in DDD
Long running processes in DDDBernd Ruecker
 
Golang - Overview of Go (golang) Language
Golang - Overview of Go (golang) LanguageGolang - Overview of Go (golang) Language
Golang - Overview of Go (golang) LanguageAniruddha Chakrabarti
 

What's hot (20)

Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in Kotlin
 
Detecting headless browsers
Detecting headless browsersDetecting headless browsers
Detecting headless browsers
 
Introduction to gRPC
Introduction to gRPCIntroduction to gRPC
Introduction to gRPC
 
gRPC Overview
gRPC OverviewgRPC Overview
gRPC Overview
 
Introduction to gRPC: A general RPC framework that puts mobile and HTTP/2 fir...
Introduction to gRPC: A general RPC framework that puts mobile and HTTP/2 fir...Introduction to gRPC: A general RPC framework that puts mobile and HTTP/2 fir...
Introduction to gRPC: A general RPC framework that puts mobile and HTTP/2 fir...
 
Coding with golang
Coding with golangCoding with golang
Coding with golang
 
REST API Design & Development
REST API Design & DevelopmentREST API Design & Development
REST API Design & Development
 
Learning jQuery in 30 minutes
Learning jQuery in 30 minutesLearning jQuery in 30 minutes
Learning jQuery in 30 minutes
 
Postman.ppt
Postman.pptPostman.ppt
Postman.ppt
 
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
 
Angular overview
Angular overviewAngular overview
Angular overview
 
AngularJS
AngularJSAngularJS
AngularJS
 
React - Introdução
React - IntroduçãoReact - Introdução
React - Introdução
 
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
Compose Camp - Jetpack Compose for Android Developers Introduction Session De...
 
Service workers
Service workersService workers
Service workers
 
HTTP2 and gRPC
HTTP2 and gRPCHTTP2 and gRPC
HTTP2 and gRPC
 
Kotlin Multiplatform
Kotlin MultiplatformKotlin Multiplatform
Kotlin Multiplatform
 
Long running processes in DDD
Long running processes in DDDLong running processes in DDD
Long running processes in DDD
 
jQuery
jQueryjQuery
jQuery
 
Golang - Overview of Go (golang) Language
Golang - Overview of Go (golang) LanguageGolang - Overview of Go (golang) Language
Golang - Overview of Go (golang) Language
 

Similar to Puppeteer can automate that! - AmsterdamJS

Puppeteer can automate that! - HolyJS Piter 2020
Puppeteer can automate that! - HolyJS Piter 2020Puppeteer can automate that! - HolyJS Piter 2020
Puppeteer can automate that! - HolyJS Piter 2020Önder Ceylan
 
Introducing perf budgets on CI with puppeteer - perf.now()
Introducing perf budgets on CI with puppeteer - perf.now()Introducing perf budgets on CI with puppeteer - perf.now()
Introducing perf budgets on CI with puppeteer - perf.now()Önder Ceylan
 
[convergese] Adaptive Images in Responsive Web Design
[convergese] Adaptive Images in Responsive Web Design[convergese] Adaptive Images in Responsive Web Design
[convergese] Adaptive Images in Responsive Web DesignChristopher Schmitt
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch
 
Bruce Lawson, Web Development 2.0, SparkUp! Poznan Poland
Bruce Lawson, Web Development 2.0, SparkUp! Poznan PolandBruce Lawson, Web Development 2.0, SparkUp! Poznan Poland
Bruce Lawson, Web Development 2.0, SparkUp! Poznan Polandbrucelawson
 
Drive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteerDrive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteerVodqaBLR
 
vodQA Pune (2019) - Browser automation using dev tools
vodQA Pune (2019) - Browser automation using dev toolsvodQA Pune (2019) - Browser automation using dev tools
vodQA Pune (2019) - Browser automation using dev toolsvodQA
 
[refreshaustin] Adaptive Images in Responsive Web Design
[refreshaustin] Adaptive Images in Responsive Web Design[refreshaustin] Adaptive Images in Responsive Web Design
[refreshaustin] Adaptive Images in Responsive Web DesignChristopher Schmitt
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETGianluca Carucci
 
Nodejs and WebSockets
Nodejs and WebSocketsNodejs and WebSockets
Nodejs and WebSocketsGonzalo Ayuso
 
Backend, app e internet das coisas com NodeJS no Google Cloud Platform
Backend, app e internet das coisas com NodeJS no Google Cloud PlatformBackend, app e internet das coisas com NodeJS no Google Cloud Platform
Backend, app e internet das coisas com NodeJS no Google Cloud PlatformDevMT
 
Backend, app e internet das coisas com NodeJS no Google Cloud Platform
Backend, app e internet das coisas com NodeJS no Google Cloud PlatformBackend, app e internet das coisas com NodeJS no Google Cloud Platform
Backend, app e internet das coisas com NodeJS no Google Cloud PlatformAlvaro Viebrantz
 
Node.js in action
Node.js in actionNode.js in action
Node.js in actionSimon Su
 
Automating Front-End Workflow
Automating Front-End WorkflowAutomating Front-End Workflow
Automating Front-End WorkflowDimitris Tsironis
 
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"Provectus
 
Javascript is your (Auto)mate
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mateCodemotion
 
Into to Node.js: Building Fast, Scaleable Network Applications
Into to Node.js: Building Fast, Scaleable Network ApplicationsInto to Node.js: Building Fast, Scaleable Network Applications
Into to Node.js: Building Fast, Scaleable Network ApplicationsFlatiron School
 

Similar to Puppeteer can automate that! - AmsterdamJS (20)

Puppeteer can automate that! - HolyJS Piter 2020
Puppeteer can automate that! - HolyJS Piter 2020Puppeteer can automate that! - HolyJS Piter 2020
Puppeteer can automate that! - HolyJS Piter 2020
 
Introducing perf budgets on CI with puppeteer - perf.now()
Introducing perf budgets on CI with puppeteer - perf.now()Introducing perf budgets on CI with puppeteer - perf.now()
Introducing perf budgets on CI with puppeteer - perf.now()
 
Node azure
Node azureNode azure
Node azure
 
JavaScript on the Desktop
JavaScript on the DesktopJavaScript on the Desktop
JavaScript on the Desktop
 
[convergese] Adaptive Images in Responsive Web Design
[convergese] Adaptive Images in Responsive Web Design[convergese] Adaptive Images in Responsive Web Design
[convergese] Adaptive Images in Responsive Web Design
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
 
Bruce Lawson, Web Development 2.0, SparkUp! Poznan Poland
Bruce Lawson, Web Development 2.0, SparkUp! Poznan PolandBruce Lawson, Web Development 2.0, SparkUp! Poznan Poland
Bruce Lawson, Web Development 2.0, SparkUp! Poznan Poland
 
Drive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteerDrive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteer
 
vodQA Pune (2019) - Browser automation using dev tools
vodQA Pune (2019) - Browser automation using dev toolsvodQA Pune (2019) - Browser automation using dev tools
vodQA Pune (2019) - Browser automation using dev tools
 
[refreshaustin] Adaptive Images in Responsive Web Design
[refreshaustin] Adaptive Images in Responsive Web Design[refreshaustin] Adaptive Images in Responsive Web Design
[refreshaustin] Adaptive Images in Responsive Web Design
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NET
 
Nodejs and WebSockets
Nodejs and WebSocketsNodejs and WebSockets
Nodejs and WebSockets
 
Backend, app e internet das coisas com NodeJS no Google Cloud Platform
Backend, app e internet das coisas com NodeJS no Google Cloud PlatformBackend, app e internet das coisas com NodeJS no Google Cloud Platform
Backend, app e internet das coisas com NodeJS no Google Cloud Platform
 
Backend, app e internet das coisas com NodeJS no Google Cloud Platform
Backend, app e internet das coisas com NodeJS no Google Cloud PlatformBackend, app e internet das coisas com NodeJS no Google Cloud Platform
Backend, app e internet das coisas com NodeJS no Google Cloud Platform
 
Node.js in action
Node.js in actionNode.js in action
Node.js in action
 
Automating Front-End Workflow
Automating Front-End WorkflowAutomating Front-End Workflow
Automating Front-End Workflow
 
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
 
Javascript is your (Auto)mate
Javascript is your (Auto)mateJavascript is your (Auto)mate
Javascript is your (Auto)mate
 
Into to Node.js: Building Fast, Scaleable Network Applications
Into to Node.js: Building Fast, Scaleable Network ApplicationsInto to Node.js: Building Fast, Scaleable Network Applications
Into to Node.js: Building Fast, Scaleable Network Applications
 

More from Önder Ceylan

Make your PWA feel more like an app
Make your PWA feel more like an appMake your PWA feel more like an app
Make your PWA feel more like an appÖnder Ceylan
 
Build a production ready PWA - LINKIT & KLM Digital Studio Meetup
Build a production ready PWA - LINKIT & KLM Digital Studio MeetupBuild a production ready PWA - LINKIT & KLM Digital Studio Meetup
Build a production ready PWA - LINKIT & KLM Digital Studio MeetupÖnder Ceylan
 
Build a production ready PWA with Angular and Firebase
Build a production ready PWA with Angular and FirebaseBuild a production ready PWA with Angular and Firebase
Build a production ready PWA with Angular and FirebaseÖnder Ceylan
 
Level up your NgRx game
Level up your NgRx gameLevel up your NgRx game
Level up your NgRx gameÖnder Ceylan
 
Building angular apps at scale
Building angular apps at scaleBuilding angular apps at scale
Building angular apps at scaleÖnder Ceylan
 
Progressive Web Apps: Is it a replacement for your mobile app?
Progressive Web Apps: Is it a replacement for your mobile app?Progressive Web Apps: Is it a replacement for your mobile app?
Progressive Web Apps: Is it a replacement for your mobile app?Önder Ceylan
 

More from Önder Ceylan (6)

Make your PWA feel more like an app
Make your PWA feel more like an appMake your PWA feel more like an app
Make your PWA feel more like an app
 
Build a production ready PWA - LINKIT & KLM Digital Studio Meetup
Build a production ready PWA - LINKIT & KLM Digital Studio MeetupBuild a production ready PWA - LINKIT & KLM Digital Studio Meetup
Build a production ready PWA - LINKIT & KLM Digital Studio Meetup
 
Build a production ready PWA with Angular and Firebase
Build a production ready PWA with Angular and FirebaseBuild a production ready PWA with Angular and Firebase
Build a production ready PWA with Angular and Firebase
 
Level up your NgRx game
Level up your NgRx gameLevel up your NgRx game
Level up your NgRx game
 
Building angular apps at scale
Building angular apps at scaleBuilding angular apps at scale
Building angular apps at scale
 
Progressive Web Apps: Is it a replacement for your mobile app?
Progressive Web Apps: Is it a replacement for your mobile app?Progressive Web Apps: Is it a replacement for your mobile app?
Progressive Web Apps: Is it a replacement for your mobile app?
 

Recently uploaded

Call Girls In Model Towh Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Model Towh Delhi 💯Call Us 🔝8264348440🔝Call Girls In Model Towh Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Model Towh Delhi 💯Call Us 🔝8264348440🔝soniya singh
 
VIP Kolkata Call Girl Kestopur 👉 8250192130 Available With Room
VIP Kolkata Call Girl Kestopur 👉 8250192130  Available With RoomVIP Kolkata Call Girl Kestopur 👉 8250192130  Available With Room
VIP Kolkata Call Girl Kestopur 👉 8250192130 Available With Roomdivyansh0kumar0
 
Low Rate Young Call Girls in Sector 63 Mamura Noida ✔️☆9289244007✔️☆ Female E...
Low Rate Young Call Girls in Sector 63 Mamura Noida ✔️☆9289244007✔️☆ Female E...Low Rate Young Call Girls in Sector 63 Mamura Noida ✔️☆9289244007✔️☆ Female E...
Low Rate Young Call Girls in Sector 63 Mamura Noida ✔️☆9289244007✔️☆ Female E...SofiyaSharma5
 
Delhi Call Girls Rohini 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
Delhi Call Girls Rohini 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip CallDelhi Call Girls Rohini 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
Delhi Call Girls Rohini 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Callshivangimorya083
 
10.pdfMature Call girls in Dubai +971563133746 Dubai Call girls
10.pdfMature Call girls in Dubai +971563133746 Dubai Call girls10.pdfMature Call girls in Dubai +971563133746 Dubai Call girls
10.pdfMature Call girls in Dubai +971563133746 Dubai Call girlsstephieert
 
Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)
Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)
Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)Dana Luther
 
Russian Call Girls in Kolkata Ishita 🤌 8250192130 🚀 Vip Call Girls Kolkata
Russian Call Girls in Kolkata Ishita 🤌  8250192130 🚀 Vip Call Girls KolkataRussian Call Girls in Kolkata Ishita 🤌  8250192130 🚀 Vip Call Girls Kolkata
Russian Call Girls in Kolkata Ishita 🤌 8250192130 🚀 Vip Call Girls Kolkataanamikaraghav4
 
FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607
FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607
FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607dollysharma2066
 
Call Girls In Mumbai Central Mumbai ❤️ 9920874524 👈 Cash on Delivery
Call Girls In Mumbai Central Mumbai ❤️ 9920874524 👈 Cash on DeliveryCall Girls In Mumbai Central Mumbai ❤️ 9920874524 👈 Cash on Delivery
Call Girls In Mumbai Central Mumbai ❤️ 9920874524 👈 Cash on Deliverybabeytanya
 
Call Girls In Saket Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Saket Delhi 💯Call Us 🔝8264348440🔝Call Girls In Saket Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Saket Delhi 💯Call Us 🔝8264348440🔝soniya singh
 
Russian Call Girls Thane Swara 8617697112 Independent Escort Service Thane
Russian Call Girls Thane Swara 8617697112 Independent Escort Service ThaneRussian Call Girls Thane Swara 8617697112 Independent Escort Service Thane
Russian Call Girls Thane Swara 8617697112 Independent Escort Service ThaneCall girls in Ahmedabad High profile
 
VIP Kolkata Call Girl Dum Dum 👉 8250192130 Available With Room
VIP Kolkata Call Girl Dum Dum 👉 8250192130  Available With RoomVIP Kolkata Call Girl Dum Dum 👉 8250192130  Available With Room
VIP Kolkata Call Girl Dum Dum 👉 8250192130 Available With Roomdivyansh0kumar0
 
Moving Beyond Twitter/X and Facebook - Social Media for local news providers
Moving Beyond Twitter/X and Facebook - Social Media for local news providersMoving Beyond Twitter/X and Facebook - Social Media for local news providers
Moving Beyond Twitter/X and Facebook - Social Media for local news providersDamian Radcliffe
 
Gram Darshan PPT cyber rural in villages of india
Gram Darshan PPT cyber rural  in villages of indiaGram Darshan PPT cyber rural  in villages of india
Gram Darshan PPT cyber rural in villages of indiaimessage0108
 

Recently uploaded (20)

Call Girls In Model Towh Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Model Towh Delhi 💯Call Us 🔝8264348440🔝Call Girls In Model Towh Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Model Towh Delhi 💯Call Us 🔝8264348440🔝
 
Rohini Sector 6 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
Rohini Sector 6 Call Girls Delhi 9999965857 @Sabina Saikh No AdvanceRohini Sector 6 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
Rohini Sector 6 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
 
Call Girls In South Ex 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SERVICE
Call Girls In South Ex 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SERVICECall Girls In South Ex 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SERVICE
Call Girls In South Ex 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SERVICE
 
VIP Kolkata Call Girl Kestopur 👉 8250192130 Available With Room
VIP Kolkata Call Girl Kestopur 👉 8250192130  Available With RoomVIP Kolkata Call Girl Kestopur 👉 8250192130  Available With Room
VIP Kolkata Call Girl Kestopur 👉 8250192130 Available With Room
 
Low Rate Young Call Girls in Sector 63 Mamura Noida ✔️☆9289244007✔️☆ Female E...
Low Rate Young Call Girls in Sector 63 Mamura Noida ✔️☆9289244007✔️☆ Female E...Low Rate Young Call Girls in Sector 63 Mamura Noida ✔️☆9289244007✔️☆ Female E...
Low Rate Young Call Girls in Sector 63 Mamura Noida ✔️☆9289244007✔️☆ Female E...
 
Rohini Sector 26 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
Rohini Sector 26 Call Girls Delhi 9999965857 @Sabina Saikh No AdvanceRohini Sector 26 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
Rohini Sector 26 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
 
Rohini Sector 22 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
Rohini Sector 22 Call Girls Delhi 9999965857 @Sabina Saikh No AdvanceRohini Sector 22 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
Rohini Sector 22 Call Girls Delhi 9999965857 @Sabina Saikh No Advance
 
Delhi Call Girls Rohini 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
Delhi Call Girls Rohini 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip CallDelhi Call Girls Rohini 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
Delhi Call Girls Rohini 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
 
10.pdfMature Call girls in Dubai +971563133746 Dubai Call girls
10.pdfMature Call girls in Dubai +971563133746 Dubai Call girls10.pdfMature Call girls in Dubai +971563133746 Dubai Call girls
10.pdfMature Call girls in Dubai +971563133746 Dubai Call girls
 
Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)
Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)
Packaging the Monolith - PHP Tek 2024 (Breaking it down one bite at a time)
 
Model Call Girl in Jamuna Vihar Delhi reach out to us at 🔝9953056974🔝
Model Call Girl in  Jamuna Vihar Delhi reach out to us at 🔝9953056974🔝Model Call Girl in  Jamuna Vihar Delhi reach out to us at 🔝9953056974🔝
Model Call Girl in Jamuna Vihar Delhi reach out to us at 🔝9953056974🔝
 
Russian Call Girls in Kolkata Ishita 🤌 8250192130 🚀 Vip Call Girls Kolkata
Russian Call Girls in Kolkata Ishita 🤌  8250192130 🚀 Vip Call Girls KolkataRussian Call Girls in Kolkata Ishita 🤌  8250192130 🚀 Vip Call Girls Kolkata
Russian Call Girls in Kolkata Ishita 🤌 8250192130 🚀 Vip Call Girls Kolkata
 
FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607
FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607
FULL ENJOY Call Girls In Mayur Vihar Delhi Contact Us 8377087607
 
Call Girls In Mumbai Central Mumbai ❤️ 9920874524 👈 Cash on Delivery
Call Girls In Mumbai Central Mumbai ❤️ 9920874524 👈 Cash on DeliveryCall Girls In Mumbai Central Mumbai ❤️ 9920874524 👈 Cash on Delivery
Call Girls In Mumbai Central Mumbai ❤️ 9920874524 👈 Cash on Delivery
 
Call Girls In Saket Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Saket Delhi 💯Call Us 🔝8264348440🔝Call Girls In Saket Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Saket Delhi 💯Call Us 🔝8264348440🔝
 
Russian Call Girls Thane Swara 8617697112 Independent Escort Service Thane
Russian Call Girls Thane Swara 8617697112 Independent Escort Service ThaneRussian Call Girls Thane Swara 8617697112 Independent Escort Service Thane
Russian Call Girls Thane Swara 8617697112 Independent Escort Service Thane
 
VIP Kolkata Call Girl Dum Dum 👉 8250192130 Available With Room
VIP Kolkata Call Girl Dum Dum 👉 8250192130  Available With RoomVIP Kolkata Call Girl Dum Dum 👉 8250192130  Available With Room
VIP Kolkata Call Girl Dum Dum 👉 8250192130 Available With Room
 
Moving Beyond Twitter/X and Facebook - Social Media for local news providers
Moving Beyond Twitter/X and Facebook - Social Media for local news providersMoving Beyond Twitter/X and Facebook - Social Media for local news providers
Moving Beyond Twitter/X and Facebook - Social Media for local news providers
 
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
 
Gram Darshan PPT cyber rural in villages of india
Gram Darshan PPT cyber rural  in villages of indiaGram Darshan PPT cyber rural  in villages of india
Gram Darshan PPT cyber rural in villages of india
 

Puppeteer can automate that! - AmsterdamJS