SlideShare a Scribd company logo
1 of 100
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 Tech 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
Xander 🇳🇱 👨🦳
Yelda 🇹🇷 👩🦱
Milena 🇷🇺 👩🦳
Text to speech!
Milena 🇷🇺 👩🦳
Text to speech!
Xander 🇳🇱 👨🦳
Text to speech!
Yelda 🇹🇷 👩🦱
speechSynthesis.getVoices()
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
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
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
Element to PDF
const overlay = await page.$(‘[data-testid="tweet"]');
await page.evaluate(tweet => {
const width = getComputedStyle(tweet).width;
tweet = tweet.cloneNode(true);
tweet.style.width = width;
document.body.innerHTML = `
<div style="display:flex;justify-content:center;align-items:center;height:100vh;">;
${tweet.outerHTML}
</div>
`;
}, overlay);
await page.pdf({path: 'tweet.pdf', printBackground: true});
Element to PDF
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
github.com/ChromeDevTools/awesome-chrome-devtools
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();
});
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
Memory leak by Prototype
// Get a handle to the Map object prototype
const mapPrototype = await page.evaluateHandle(() => Map.prototype);
// Query all map instances into an array
const mapInstances = await page.queryObjects(mapPrototype);
// Count amount of map objects in heap
const count = await page.evaluate(maps => maps.length, mapInstances);
// Idea here is to test object instances on the page
// where it's expected to be invalidated
expect(count).toBe(0);
Memory leak by Prototype
Monitor change on security state
const protocol = await page.target().createCDPSession();
await protocol.send('Security.enable');
protocol.on('Security.securityStateChanged', console.log);
Monitor change on security state
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 server environments —
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!
@onderceylan

More Related Content

What's hot

Brave new world of HTML5
Brave new world of HTML5Brave new world of HTML5
Brave new world of HTML5Chris Mills
 
Instant and offline apps with Service Worker
Instant and offline apps with Service WorkerInstant and offline apps with Service Worker
Instant and offline apps with Service WorkerChang W. Doh
 
Puppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing ToolPuppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing ToolMiki Lombardi
 
WebGL For Game Development Spring 2013
WebGL For Game Development Spring 2013WebGL For Game Development Spring 2013
WebGL For Game Development Spring 2013Tony Parisi
 
Chrome enchanted 2015
Chrome enchanted 2015Chrome enchanted 2015
Chrome enchanted 2015Chang W. Doh
 
HTML5 & The Open Web - at Nackademin
HTML5 & The Open Web -  at NackademinHTML5 & The Open Web -  at Nackademin
HTML5 & The Open Web - at NackademinRobert Nyman
 
Making your Angular.js Application accessible
Making your Angular.js Application accessibleMaking your Angular.js Application accessible
Making your Angular.js Application accessibleDirk Ginader
 
FINHTML5 - Breaking the mobile web
FINHTML5 - Breaking the mobile webFINHTML5 - Breaking the mobile web
FINHTML5 - Breaking the mobile webMaximiliano Firtman
 
[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
 
[jqconatx] Adaptive Images for Responsive Web Design
[jqconatx] Adaptive Images for Responsive Web Design[jqconatx] Adaptive Images for Responsive Web Design
[jqconatx] Adaptive Images for Responsive Web DesignChristopher Schmitt
 
Node.js & Twitter Bootstrap Crash Course
Node.js & Twitter Bootstrap Crash CourseNode.js & Twitter Bootstrap Crash Course
Node.js & Twitter Bootstrap Crash CourseAaron Silverman
 
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other ToolsCool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other ToolsRyan Weaver
 
Html5 Game Development with Canvas
Html5 Game Development with CanvasHtml5 Game Development with Canvas
Html5 Game Development with CanvasPham Huy Tung
 
Collective.amberjack ploneconf2010
Collective.amberjack ploneconf2010Collective.amberjack ploneconf2010
Collective.amberjack ploneconf2010Massimo Azzolini
 
JavaScript APIs - The Web is the Platform - MDN Hack Day, Sao Paulo
JavaScript APIs - The Web is the Platform - MDN Hack Day, Sao PauloJavaScript APIs - The Web is the Platform - MDN Hack Day, Sao Paulo
JavaScript APIs - The Web is the Platform - MDN Hack Day, Sao PauloRobert Nyman
 
Leave No One Behind with HTML5 - FFWD.PRO, Croatia
Leave No One Behind with HTML5 - FFWD.PRO, CroatiaLeave No One Behind with HTML5 - FFWD.PRO, Croatia
Leave No One Behind with HTML5 - FFWD.PRO, CroatiaRobert Nyman
 
Goodbye, Flatland! An introduction to React VR and what it means for web dev...
Goodbye, Flatland! An introduction to React VR  and what it means for web dev...Goodbye, Flatland! An introduction to React VR  and what it means for web dev...
Goodbye, Flatland! An introduction to React VR and what it means for web dev...GeilDanke
 

What's hot (20)

Brave new world of HTML5
Brave new world of HTML5Brave new world of HTML5
Brave new world of HTML5
 
Instant and offline apps with Service Worker
Instant and offline apps with Service WorkerInstant and offline apps with Service Worker
Instant and offline apps with Service Worker
 
Puppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing ToolPuppeteer - A web scraping & UI Testing Tool
Puppeteer - A web scraping & UI Testing Tool
 
WebGL For Game Development Spring 2013
WebGL For Game Development Spring 2013WebGL For Game Development Spring 2013
WebGL For Game Development Spring 2013
 
Puppeteer
PuppeteerPuppeteer
Puppeteer
 
Chrome enchanted 2015
Chrome enchanted 2015Chrome enchanted 2015
Chrome enchanted 2015
 
HTML5 & The Open Web - at Nackademin
HTML5 & The Open Web -  at NackademinHTML5 & The Open Web -  at Nackademin
HTML5 & The Open Web - at Nackademin
 
Making your Angular.js Application accessible
Making your Angular.js Application accessibleMaking your Angular.js Application accessible
Making your Angular.js Application accessible
 
FINHTML5 - Breaking the mobile web
FINHTML5 - Breaking the mobile webFINHTML5 - Breaking the mobile web
FINHTML5 - Breaking the mobile web
 
[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
 
[jqconatx] Adaptive Images for Responsive Web Design
[jqconatx] Adaptive Images for Responsive Web Design[jqconatx] Adaptive Images for Responsive Web Design
[jqconatx] Adaptive Images for Responsive Web Design
 
Node.js & Twitter Bootstrap Crash Course
Node.js & Twitter Bootstrap Crash CourseNode.js & Twitter Bootstrap Crash Course
Node.js & Twitter Bootstrap Crash Course
 
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other ToolsCool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
 
Html5 Game Development with Canvas
Html5 Game Development with CanvasHtml5 Game Development with Canvas
Html5 Game Development with Canvas
 
Nodejs.meetup
Nodejs.meetupNodejs.meetup
Nodejs.meetup
 
Mastering Grunt
Mastering GruntMastering Grunt
Mastering Grunt
 
Collective.amberjack ploneconf2010
Collective.amberjack ploneconf2010Collective.amberjack ploneconf2010
Collective.amberjack ploneconf2010
 
JavaScript APIs - The Web is the Platform - MDN Hack Day, Sao Paulo
JavaScript APIs - The Web is the Platform - MDN Hack Day, Sao PauloJavaScript APIs - The Web is the Platform - MDN Hack Day, Sao Paulo
JavaScript APIs - The Web is the Platform - MDN Hack Day, Sao Paulo
 
Leave No One Behind with HTML5 - FFWD.PRO, Croatia
Leave No One Behind with HTML5 - FFWD.PRO, CroatiaLeave No One Behind with HTML5 - FFWD.PRO, Croatia
Leave No One Behind with HTML5 - FFWD.PRO, Croatia
 
Goodbye, Flatland! An introduction to React VR and what it means for web dev...
Goodbye, Flatland! An introduction to React VR  and what it means for web dev...Goodbye, Flatland! An introduction to React VR  and what it means for web dev...
Goodbye, Flatland! An introduction to React VR and what it means for web dev...
 

Similar to 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()Önder Ceylan
 
Puppeteer - Headless Chrome Node API
Puppeteer - Headless Chrome Node APIPuppeteer - Headless Chrome Node API
Puppeteer - Headless Chrome Node APIWilson Su
 
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
 
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
 
Pinkoi Mobile Web
Pinkoi Mobile WebPinkoi Mobile Web
Pinkoi Mobile Webmikeleeme
 
Drive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteerDrive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteerVodqaBLR
 
Service Worker - Reliability bits
Service Worker - Reliability bitsService Worker - Reliability bits
Service Worker - Reliability bitsjungkees
 
Intro To webOS
Intro To webOSIntro To webOS
Intro To webOSfpatton
 
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
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applicationsTom Croucher
 
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
 
Mobile App Development: Primi passi con NativeScript e Angular 2
Mobile App Development: Primi passi con NativeScript e Angular 2Mobile App Development: Primi passi con NativeScript e Angular 2
Mobile App Development: Primi passi con NativeScript e Angular 2Filippo Matteo Riggio
 
Nodejs and WebSockets
Nodejs and WebSocketsNodejs and WebSockets
Nodejs and WebSocketsGonzalo Ayuso
 
AFUP Lorraine - Symfony Webpack Encore
AFUP Lorraine - Symfony Webpack EncoreAFUP Lorraine - Symfony Webpack Encore
AFUP Lorraine - Symfony Webpack EncoreEngineor
 
Ten practical ways to improve front-end performance
Ten practical ways to improve front-end performanceTen practical ways to improve front-end performance
Ten practical ways to improve front-end performanceAndrew Rota
 
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"Provectus
 

Similar to Puppeteer can automate that! - HolyJS Piter 2020 (20)

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()
 
Puppeteer - Headless Chrome Node API
Puppeteer - Headless Chrome Node APIPuppeteer - Headless Chrome Node API
Puppeteer - Headless Chrome Node API
 
JavaScript on the Desktop
JavaScript on the DesktopJavaScript on the Desktop
JavaScript on the Desktop
 
Node azure
Node azureNode azure
Node azure
 
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
 
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
 
Always on! Or not?
Always on! Or not?Always on! Or not?
Always on! Or not?
 
Pinkoi Mobile Web
Pinkoi Mobile WebPinkoi Mobile Web
Pinkoi Mobile Web
 
Drive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteerDrive chrome(headless) with puppeteer
Drive chrome(headless) with puppeteer
 
HTML5 WebWorks
HTML5 WebWorksHTML5 WebWorks
HTML5 WebWorks
 
Service Worker - Reliability bits
Service Worker - Reliability bitsService Worker - Reliability bits
Service Worker - Reliability bits
 
Intro To webOS
Intro To webOSIntro To webOS
Intro To webOS
 
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
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
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
 
Mobile App Development: Primi passi con NativeScript e Angular 2
Mobile App Development: Primi passi con NativeScript e Angular 2Mobile App Development: Primi passi con NativeScript e Angular 2
Mobile App Development: Primi passi con NativeScript e Angular 2
 
Nodejs and WebSockets
Nodejs and WebSocketsNodejs and WebSockets
Nodejs and WebSockets
 
AFUP Lorraine - Symfony Webpack Encore
AFUP Lorraine - Symfony Webpack EncoreAFUP Lorraine - Symfony Webpack Encore
AFUP Lorraine - Symfony Webpack Encore
 
Ten practical ways to improve front-end performance
Ten practical ways to improve front-end performanceTen practical ways to improve front-end performance
Ten practical ways to improve front-end performance
 
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
Василевский Илья (Fun-box): "автоматизация браузера при помощи PhantomJS"
 

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

Film cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasaFilm cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasa494f574xmv
 
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一z xss
 
Call Girls South Delhi Delhi reach out to us at ☎ 9711199012
Call Girls South Delhi Delhi reach out to us at ☎ 9711199012Call Girls South Delhi Delhi reach out to us at ☎ 9711199012
Call Girls South Delhi Delhi reach out to us at ☎ 9711199012rehmti665
 
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一Fs
 
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一Fs
 
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作ys8omjxb
 
Call Girls in Uttam Nagar Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Uttam Nagar Delhi 💯Call Us 🔝8264348440🔝Call Girls in Uttam Nagar Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Uttam Nagar Delhi 💯Call Us 🔝8264348440🔝soniya singh
 
Git and Github workshop GDSC MLRITM
Git and Github  workshop GDSC MLRITMGit and Github  workshop GDSC MLRITM
Git and Github workshop GDSC MLRITMgdsc13
 
定制(UAL学位证)英国伦敦艺术大学毕业证成绩单原版一比一
定制(UAL学位证)英国伦敦艺术大学毕业证成绩单原版一比一定制(UAL学位证)英国伦敦艺术大学毕业证成绩单原版一比一
定制(UAL学位证)英国伦敦艺术大学毕业证成绩单原版一比一Fs
 
Contact Rya Baby for Call Girls New Delhi
Contact Rya Baby for Call Girls New DelhiContact Rya Baby for Call Girls New Delhi
Contact Rya Baby for Call Girls New Delhimiss dipika
 
Blepharitis inflammation of eyelid symptoms cause everything included along w...
Blepharitis inflammation of eyelid symptoms cause everything included along w...Blepharitis inflammation of eyelid symptoms cause everything included along w...
Blepharitis inflammation of eyelid symptoms cause everything included along w...Excelmac1
 
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
 
Top 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxTop 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxDyna Gilbert
 
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一Fs
 
Font Performance - NYC WebPerf Meetup April '24
Font Performance - NYC WebPerf Meetup April '24Font Performance - NYC WebPerf Meetup April '24
Font Performance - NYC WebPerf Meetup April '24Paul Calvano
 
PHP-based rendering of TYPO3 Documentation
PHP-based rendering of TYPO3 DocumentationPHP-based rendering of TYPO3 Documentation
PHP-based rendering of TYPO3 DocumentationLinaWolf1
 
Magic exist by Marta Loveguard - presentation.pptx
Magic exist by Marta Loveguard - presentation.pptxMagic exist by Marta Loveguard - presentation.pptx
Magic exist by Marta Loveguard - presentation.pptxMartaLoveguard
 

Recently uploaded (20)

Film cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasaFilm cover research (1).pptxsdasdasdasdasdasa
Film cover research (1).pptxsdasdasdasdasdasa
 
Hot Sexy call girls in Rk Puram 🔝 9953056974 🔝 Delhi escort Service
Hot Sexy call girls in  Rk Puram 🔝 9953056974 🔝 Delhi escort ServiceHot Sexy call girls in  Rk Puram 🔝 9953056974 🔝 Delhi escort Service
Hot Sexy call girls in Rk Puram 🔝 9953056974 🔝 Delhi escort Service
 
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
办理(UofR毕业证书)罗切斯特大学毕业证成绩单原版一比一
 
Call Girls South Delhi Delhi reach out to us at ☎ 9711199012
Call Girls South Delhi Delhi reach out to us at ☎ 9711199012Call Girls South Delhi Delhi reach out to us at ☎ 9711199012
Call Girls South Delhi Delhi reach out to us at ☎ 9711199012
 
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
定制(AUT毕业证书)新西兰奥克兰理工大学毕业证成绩单原版一比一
 
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🔝
 
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
定制(Management毕业证书)新加坡管理大学毕业证成绩单原版一比一
 
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
Potsdam FH学位证,波茨坦应用技术大学毕业证书1:1制作
 
Call Girls in Uttam Nagar Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Uttam Nagar Delhi 💯Call Us 🔝8264348440🔝Call Girls in Uttam Nagar Delhi 💯Call Us 🔝8264348440🔝
Call Girls in Uttam Nagar Delhi 💯Call Us 🔝8264348440🔝
 
Git and Github workshop GDSC MLRITM
Git and Github  workshop GDSC MLRITMGit and Github  workshop GDSC MLRITM
Git and Github workshop GDSC MLRITM
 
定制(UAL学位证)英国伦敦艺术大学毕业证成绩单原版一比一
定制(UAL学位证)英国伦敦艺术大学毕业证成绩单原版一比一定制(UAL学位证)英国伦敦艺术大学毕业证成绩单原版一比一
定制(UAL学位证)英国伦敦艺术大学毕业证成绩单原版一比一
 
Contact Rya Baby for Call Girls New Delhi
Contact Rya Baby for Call Girls New DelhiContact Rya Baby for Call Girls New Delhi
Contact Rya Baby for Call Girls New Delhi
 
Blepharitis inflammation of eyelid symptoms cause everything included along w...
Blepharitis inflammation of eyelid symptoms cause everything included along w...Blepharitis inflammation of eyelid symptoms cause everything included along w...
Blepharitis inflammation of eyelid symptoms cause everything included along w...
 
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)
 
Top 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptxTop 10 Interactive Website Design Trends in 2024.pptx
Top 10 Interactive Website Design Trends in 2024.pptx
 
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
定制(Lincoln毕业证书)新西兰林肯大学毕业证成绩单原版一比一
 
Font Performance - NYC WebPerf Meetup April '24
Font Performance - NYC WebPerf Meetup April '24Font Performance - NYC WebPerf Meetup April '24
Font Performance - NYC WebPerf Meetup April '24
 
PHP-based rendering of TYPO3 Documentation
PHP-based rendering of TYPO3 DocumentationPHP-based rendering of TYPO3 Documentation
PHP-based rendering of TYPO3 Documentation
 
Magic exist by Marta Loveguard - presentation.pptx
Magic exist by Marta Loveguard - presentation.pptxMagic exist by Marta Loveguard - presentation.pptx
Magic exist by Marta Loveguard - presentation.pptx
 
young call girls in Uttam Nagar🔝 9953056974 🔝 Delhi escort Service
young call girls in Uttam Nagar🔝 9953056974 🔝 Delhi escort Serviceyoung call girls in Uttam Nagar🔝 9953056974 🔝 Delhi escort Service
young call girls in Uttam Nagar🔝 9953056974 🔝 Delhi escort Service
 

Puppeteer can automate that! - HolyJS Piter 2020