Closed jandppw closed 2 years ago
Just made the afterAll hook wait for 20s:
all tests done; closing remaining chromium processes
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125427619,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"CALLED"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125427619,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"FOUND_BROWSER_PROMISE"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125427619,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"DISCARDED_BROWSER_PROMISE"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125427620,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"BROWSER_PROMISE_RESOLVED"}
1) "after all" hook: afterAll in "{root}"
4 passing (23s)
1 failing
1) "after all" hook: afterAll in "{root}":
Error: Timeout of 20000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
at listOnTimeout (internal/timers.js:557:17)
at processTimers (internal/timers.js:500:7)
Even after 20s, the close()
command does not resolve, at the end of the test run.
I now have a limited test suite, that only does some limited tests with the browser. One of the tests is testing whether ensureBrowserClosed
works, and it does in that test, repeatedly:
/* eslint-env mocha */
const testName = require('../../_testName')
const { getBrowser, ensureBrowserClosed } = require('../../../lib/pdf/browser')
const flowId = '91b5ee9c-632d-4af4-8154-ddd2490bf677'
const zeroTo9 = Array(10)
.fill()
.map((ignore, idx) => idx)
describe(testName(module), function () {
this.timeout(20000) // starting the browser takes a few seconds
after(async function () {
return ensureBrowserClosed() // <<< THIS WORKS
})
it('always returns the same browser promise and browser', async function () {
const browserPromises = zeroTo9.map(i => getBrowser(flowId))
browserPromises.forEach(bp => {
bp.should.equal(browserPromises[0])
})
const browsers = await Promise.all(browserPromises)
browsers.forEach(b => {
b.isConnected().should.be.true()
b.should.equal(browsers[0])
})
// after await
const browserPromises2 = zeroTo9.map(i => getBrowser(flowId))
browserPromises2.forEach(bp => {
bp.should.equal(browserPromises[0])
})
const browsers2 = await Promise.all(browserPromises2)
browsers2.forEach(b => {
b.isConnected().should.be.true()
b.should.equal(browsers[0])
})
})
it('discards an existing browser', async function () {
const browser1Promise = getBrowser(flowId)
const browser1 = await browser1Promise
browser1.isConnected().should.be.true()
const closedPromise = ensureBrowserClosed() // <<< THIS WORKS
const browser2Promise = getBrowser(flowId)
browser2Promise.should.not.equal(browser1Promise)
await closedPromise
browser1.isConnected().should.be.false()
const browser2 = await browser2Promise
browser2.isConnected().should.be.true()
browser2.should.not.equal(browser1)
})
it('does not fail when we try to close a browser if there is none', async function () {
await ensureBrowserClosed(flowId)
await ensureBrowserClosed(flowId) // this doesn't fail
})
})
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125424998,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser","phase":"CALLED"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125424998,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser","phase":"NOMINAL_END"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125424998,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"CALLED"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125424999,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"FOUND_BROWSER_PROMISE"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125424999,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"DISCARDED_BROWSER_PROMISE"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125424999,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser","phase":"CALLED"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125424999,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser#launchAndReport","phase":"ENVIRONMENT_DETERMINED","details":{"environment":"bitbucket","noSandbox":true}}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125425000,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser#launchAndReport","phase":"ADDING_FONTS","details":{"fontFilePaths":["/opt/atlassian/pipelines/agent/build/lib/pdf/fonts/Roboto/Roboto-Bold.ttf","/opt/atlassian/pipelines/agent/build/lib/pdf/fonts/Roboto/Roboto-BoldItalic.ttf","/opt/atlassian/pipelines/agent/build/lib/pdf/fonts/Roboto/Roboto-Italic.ttf","/opt/atlassian/pipelines/agent/build/lib/pdf/fonts/Roboto/Roboto-Regular.ttf"]}}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125425000,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser","phase":"NOMINAL_END"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125425000,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"BROWSER_PROMISE_RESOLVED"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125425001,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser#launchAndReport","phase":"FONTS_ADDED","details":{"appliedFonts":[null,null,null,null]}}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125425002,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser#launchAndReport","phase":"LAUNCHING_BROWSER","details":{"launchOptions":{"executablePath":null,"args":["--allow-running-insecure-content","--autoplay-policy=user-gesture-required","--disable-component-update","--disable-domain-reliability","--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process","--disable-print-preview","--disable-setuid-sandbox","--disable-site-isolation-trials","--disable-speech-api","--disable-web-security","--disk-cache-size=33554432","--enable-features=SharedArrayBuffer","--hide-scrollbars","--ignore-gpu-blocklist","--in-process-gpu","--mute-audio","--no-default-browser-check","--no-pings","--no-sandbox","--no-zygote","--use-gl=angle","--use-angle=swiftshader","--window-size=1920,1080","--start-maximized"],"defaultViewport":{"deviceScaleFactor":1,"hasTouch":false,"height":1080,"isLandscape":true,"isMobile":false,"width":1920},"headless":true}}}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125425149,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"BROWSER_CLOSED"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125425149,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"NOMINAL_END"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"flowId":"91b5ee9c-632d-4af4-8154-ddd2490bf677","now":1655125425175,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"getBrowser#launchAndReport","phase":"NOMINAL_END","details":{"version":"HeadlessChrome/103.0.5058.0","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/103.0.5058.0 Safari/537.36"}}
✔ discards an existing browser (178ms)
Yet, at the end of the run:
all tests done; closing remaining chromium processes
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125427619,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"CALLED"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125427619,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"FOUND_BROWSER_PROMISE"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125427619,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"DISCARDED_BROWSER_PROMISE"}
{"lambda":{"instanceId":"77717a25-5031-4be8-98b0-3004e1310f1e"},"now":1655125427620,"level":"INFO","module":"/opt/atlassian/pipelines/agent/build/lib/pdf/browser.js","function":"ensureBrowserClosed","phase":"BROWSER_PROMISE_RESOLVED"}
1) "after all" hook: afterAll in "{root}"
4 passing (23s)
1 failing
1) "after all" hook: afterAll in "{root}":
Error: Timeout of 20000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
at listOnTimeout (internal/timers.js:557:17)
at processTimers (internal/timers.js:500:7)
Note that in this test, I never open a window, or generate a PDF, …, but I do in other tests in this run, that come later.
Now, I have ran a test suite with only 1 test, where I do use the browser. In this case, await browser.close()
does not resolve.
So it seems that in this version of this package, something happens that prevents closing the browser once we did "something" with the browser.
The test code is:
/* eslint-env mocha */
const testName = require('../../_testName')
const createAndStorePDF = require('../../../lib/pdf/createAndStorePDF')
const { v4: uuidv4 } = require('uuid')
const { example: html } = require('../../../lib/ppwcode/HTML')
const checkObject = require('../05.aws/_checkObject')
const deleteObject = require('../05.aws/_deleteObject')
const { cacheControlNoStore } = require('../../../lib/lambda/apigateway/ResponseHeaders')
describe(testName(module), function () {
/**
* @typedef {object} Fixture
* @property {UUID} flowId
* @property {string} path
* @property {string} filename
*/
beforeEach(
/** @this {Fixture & Context} */ async function () {
this.timeout(5000)
this.flowId = uuidv4()
this.path = `automated-test/getSignedDownloadURL/${this.flowId}`
this.filename = `automated-test-getSignedDownloadURL-${this.flowId}.pdf`
}
)
afterEach(
/** @this {Fixture} */ async function () {
await deleteObject(this.path)
}
)
it('works', /** @this {Fixture & Context} */ async function () {
this.timeout(10000)
const sot = new Date().toISOString()
await createAndStorePDF(sot, this.flowId, this.path, html, cacheControlNoStore, this.filename)
await checkObject(this.flowId, this.path, cacheControlNoStore, this.filename)
})
})
which calls
const log = require('../ppwcode/simpleLog')
const { getBrowser } = require('./browser')
const storeInS3 = require('../aws/storeInS3')
const pdfOptions = require('./pdfOptions')
/**
* @callback createAndStorePDF
* @param {PointInTime} sot
* @param {UUID} flowId
* @param {string} s3Path
* @param {HTML} html
* @param {CacheControl} cacheControl
* @param {string} filename
* @return {Promise<undefined>}
*/
/**
* Create a PDF for the given `html`, and store it in S3 in the `s3Path`, with meta-information for download,
* such as `cache-control` and `content-disposition = "at
*
* @type {createAndStorePDF}
*/
async function createAndStorePDF (sot, flowId, s3Path, html, cacheControl, filename) {
log.info(module, 'createAndStorePDF', flowId, 'CALLED', { sot, s3Path, cacheControl, filename })
const browser = await getBrowser(flowId)
const page = await browser.newPage()
log.info(module, 'createAndStorePDF', flowId, 'CHROMIUM_PAGE_CREATED')
/* NOTE: We need to wait for external content to load before we generate a PDF. Notably, we use web fonts.
The `load` event is fired when the whole page has loaded, including all dependent resources such as
stylesheets and images. This is in contrast to `DOMContentLoaded`, which is fired as soon as the page DOM has
been loaded, without waiting for resources to finish loading. */
await page.setContent(html, { waitUntil: 'load' })
log.info(module, 'createAndStorePDF', flowId, 'CHROMIUM_CONTENT_LOADED')
const pdf = await page.pdf(pdfOptions)
log.info(module, 'createAndStorePDF', flowId, 'CHROMIUM_PDF_GENERATED')
await Promise.all([
(async () => {
await page.close()
log.info(module, 'createAndStorePDF', flowId, 'CHROMIUM_PAGE_CLOSED')
})(),
storeInS3(flowId, s3Path, cacheControl, filename, pdf)
])
log.info(module, 'createAndStorePDF', flowId, 'NOMINAL_END')
}
module.exports = createAndStorePDF
Next, I limited the test to not creating a PDF, and after that, to not loading the page, and after that, to not even closing the page.
When I only create the page
const page = await browser.newPage()
browser.close()
does not resolve.
Finally, even removed creating a page. And it still fails.
So I am at a loss here. In direct tests, closing resolves. Indirectly, it doesn't.
Stripped the test further, bypassing Sparticuz/chrome-aws-lambda
completely, talking directly to puppeteer
.
Still fails in the indirect test.
In other words, nevermind. This isn't an issue with Sparticuz/chrome-aws-lambda
. Sorry for wasting your time.
Still fails in the indirect test.
I've also never gotten browser.close()
to work...but I'm not sure I care because the lambda instance terminates anyway.
@Sparticuz Thx for the reply. Now I know I'm not crazy, at least.
Yes, in production that is true. But I need to be able to kill the Chromium process in the CI tests, or otherwise the mocha run never stops, processes hang, the run does not progress to the next steps, …
Wait a sec. Is that even true? I want to stop Chrome certainly locally, because otherwise I get myriads of chromes running when developing, but that works. But in a container on CI? Maybe it will just kill it for me at the end?
In any case, it's probably a Puppeteer thing, so I'll take my whining there.
I have this issue as well in lambdas - the lambda instance ends up running until it reaches timeout (~30s), kind of annoying but a pdf is returned well before the limit is reached.
Environment
chrome-aws-lambda
Version:@sparticuz/chrome-aws-lambda
14.3.0
puppeteer
/puppeteer-core
Version:14.3.0
node:14
on Bitbucket Pipelines)node:14
)Expected Behavior
await browser.close()
should resolveCurrent Behavior
await browser.close()
does not resolveSteps to Reproduce
Thank you very much, @sparticuz, for picking up from @alixaxel.
I have been using the last version of the original chrome-aws-lambda for about 2 years now (
10.1.0
). Automated tests withmocha
run, and among other things, start up a browser, and finally, after all tests have run, close that browser. These tests run locally (macOS, Windows) and on Bitbucket Pipelines, in a Node Docker container.I am now trying to switch to your fork so we can evolve with Chromium and Puppeteer.
All tests still run fine on macOS, and in Bitbucket Pipelines, in the
node:14
Docker container, and all tests pass, both with chrome-aws-lambda@10.1.0 and /@sparticuz/chrome-aws-lambda@14.3.0 (not tested on Windows lately).Yet, with chrome-aws-lambda@10.1.0 the browser used for the tests closes finally. With /@sparticuz/chrome-aws-lambda@14.3.0, it does not.
The relevant Bitbucket Pipelines step definition is:
The test script is:
In the
mocha
configuration, a globalafterAll
is set up:which is:
The code that (should) close the browser (and did so with
chrome-aws-lambda@10.1.0
) isensureBrowserClosed
:The relevant output we when the tests are run on macOS is:
On Bitbucket Pipelines, the comparable output is:
This must mean that
await browser.close()
hangs for quite a long time.Any ideas on why this might occur in the updated version?