Skip to content

Commit a708702

Browse files
authored
feat: add font loading (#18)
1 parent 6fb9161 commit a708702

15 files changed

+203
-64
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ jobs:
2020
- name: Build project
2121
run: yarn run build
2222
- name: Test lambda function
23-
run: bash buildbots/stub.sh
23+
run: bash buildbots/test.sh

README.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# playwright-aws-lambda
22

3-
Support for PlayWright running on AWS Lambda and Google Cloud Functions.
3+
Support for Playwright running on AWS Lambda and Google Cloud Functions.
44

5-
NOTE: Currently only Chromium is supported.
5+
**NOTE**: Currently only Chromium is supported.
66

77
## Install
88

@@ -12,8 +12,8 @@ npm install playwright-core playwright-aws-lambda --save
1212

1313
## Usage
1414

15-
This package works with the `nodejs8.10`, `nodejs10.x` and `nodejs12.x` AWS
16-
Lambda runtimes out of the box.
15+
This package works with the `nodejs10.x` and `nodejs12.x` AWS Lambda runtimes
16+
out of the box.
1717

1818
```javascript
1919
const playwright = require('playwright-aws-lambda');
@@ -41,9 +41,22 @@ exports.handler = async (event, context) => {
4141

4242
## API
4343

44-
| Method / Property | Returns | Description |
45-
| ----------------- | ---------------------------------------- | ------------------------------ |
46-
| `launchChromium` | `{!Promise<playwright.ChromiumBrowser>}` | Launches the Chromium browser. |
44+
| Method / Property | Returns | Description |
45+
| ----------------- | ---------------------------------------- | ------------------------------------- |
46+
| `launchChromium` | `{!Promise<playwright.ChromiumBrowser>}` | Launches the Chromium browser. |
47+
| `loadFont(url)` | `{Promise<void>}` | Downloads and activates a custom font |
48+
49+
### Loading additional fonts
50+
51+
If you need custom font support by e.g. emojicons in your browser, you have to
52+
load it by using the `loadFont(url: string)` function before you launch the
53+
browser.
54+
55+
```js
56+
await loadFont(
57+
'https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf'
58+
);
59+
```
4760

4861
## Thanks / Credits
4962

buildbots/assets/emoji.png

870 Bytes
Loading

examples/stub.js renamed to buildbots/assets/test-default.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
const playwright = require('../dist/src/');
1+
const playwright = require('../../dist/src/');
2+
const assert = require('assert');
23

34
exports.handler = async (event, context) => {
45
let browser = null;
@@ -9,11 +10,16 @@ exports.handler = async (event, context) => {
910

1011
const page = await context.newPage();
1112
await page.goto(event.url || 'https://example.com');
13+
1214
const data = await page.screenshot();
13-
if (data.length === 0) {
14-
throw new Error(`Screenshot is empty`);
15-
}
16-
console.log('Page title: ', await page.title());
15+
assert(data.length > 0, 'Page screenshot is empty');
16+
17+
assert((await page.title()) === 'Google', 'Title does not match');
18+
19+
// Test so it does not crash
20+
await page.setContent('<span id="icon">🎭</span>');
21+
22+
console.log('inside-lambda: Passed tests');
1723
} catch (error) {
1824
throw error;
1925
} finally {

buildbots/assets/test-font.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const playwright = require('../../dist/src/');
2+
const assert = require('assert');
3+
const fs = require('fs');
4+
const PNG = require('pngjs').PNG;
5+
const pixelmatch = require('pixelmatch');
6+
7+
exports.handler = async (event, context) => {
8+
let browser = null;
9+
10+
try {
11+
await playwright.loadFont(
12+
'https://raw.githack.com/googlei18n/noto-emoji/master/fonts/NotoColorEmoji.ttf'
13+
);
14+
const browser = await playwright.launchChromium();
15+
const context = await browser.newContext();
16+
17+
const page = await context.newPage();
18+
await page.goto(event.url || 'https://example.com');
19+
20+
const data = await page.screenshot();
21+
assert(data.length > 0, 'Page screenshot is empty');
22+
23+
assert((await page.title()) === 'Google', 'Title does not match');
24+
25+
await page.setContent('<span id="icon">🎭</span>');
26+
const emojiImage = await (await page.$('#icon')).screenshot();
27+
const img1 = PNG.sync.read(fs.readFileSync('buildbots/assets/emoji.png'));
28+
const img2 = PNG.sync.read(emojiImage);
29+
const { width, height } = img1;
30+
31+
const imageMatches = pixelmatch(img1.data, img2.data, null, width, height, {
32+
threshold: 0.1,
33+
});
34+
assert(!imageMatches, 'font loading with emojis does not work');
35+
36+
console.log('inside-lambda: Passed tests');
37+
} catch (error) {
38+
throw error;
39+
} finally {
40+
if (browser !== null) {
41+
await browser.close();
42+
}
43+
}
44+
};

buildbots/stub.sh

Lines changed: 0 additions & 11 deletions
This file was deleted.

buildbots/test.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#/bin/bash
2+
3+
set -ex
4+
5+
LOG_FILE="log.txt"
6+
7+
function run_test {
8+
test_file="$1"
9+
echo "Testing $newfile against $oldfile"
10+
11+
echo "Running test: $test_file"
12+
docker run --rm -v "$PWD":/var/task:delegated lambci/lambda:nodejs12.x "buildbots/assets/$test_file.handler" '{"url": "https://google.com"}' 2>&1 | tee "$LOG_FILE"
13+
grep -Fq "inside-lambda: Passed tests" "$LOG_FILE"
14+
15+
echo "Sucessfully passed test: $test_file"
16+
}
17+
18+
run_test "test-font"
19+
run_test "test-default"
20+
21+
rm "$LOG_FILE"

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
"jest": "^26.3.0",
3636
"lint-staged": "10.2.11",
3737
"nodemon": "^2.0.4",
38+
"pixelmatch": "^5.2.1",
3839
"playwright-core": "^1.3.0",
40+
"pngjs": "^5.0.0",
3941
"prettier": "^2.0.5",
4042
"ts-jest": "^26.2.0",
4143
"tslint": "^6.1.3",

src/chromium.ts

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
import { promises as fsPromises } from 'fs';
2-
import { join } from 'path';
2+
import * as fs from 'fs';
3+
import * as path from 'path';
4+
import { promisify } from 'util';
5+
import * as https from 'https';
6+
37
import * as playwright from 'playwright-core';
8+
import { LaunchOptions } from 'playwright-core';
9+
410
import isLambdaRuntimeEnvironment from './util/isLambdaRuntimeEnvironment';
511
import isHeadlessModeEnabled from './util/isHeadlessModeEnabled';
612
import fileExists from './util/fileExists';
7-
import setEnvironmentVariables from './util/setEnvironmentVariables';
8-
import { LaunchOptions } from 'playwright-core';
13+
import getEnvironmentVariables, {
14+
AWS_FONT_DIR,
15+
} from './util/getEnvironmentVariables';
916

1017
const { inflate } = require('lambdafs');
1118

12-
setEnvironmentVariables();
13-
1419
/**
1520
* Returns a list of recommended additional Chromium flags.
1621
*/
@@ -80,7 +85,7 @@ async function getChromiumExecutablePath(
8085
return '/tmp/chromium';
8186
}
8287

83-
const input = join(__dirname, 'bin');
88+
const input = path.join(__dirname, 'bin');
8489
const promises = [
8590
inflate(`${input}/chromium.br`),
8691
inflate(`${input}/swiftshader.tar.br`),
@@ -99,12 +104,45 @@ export async function launchChromium(launchOptions?: Partial<LaunchOptions>) {
99104
const args = getChromiumArgs(headless);
100105
const executablePath = await getChromiumExecutablePath(headless);
101106

107+
const env: LaunchOptions['env'] = {
108+
...(await getEnvironmentVariables()),
109+
...(launchOptions?.env || {}),
110+
};
102111
const browser = await playwright.chromium.launch({
103112
args,
104113
executablePath,
105114
headless,
115+
env,
106116
...launchOptions,
107117
});
108118

109119
return browser;
110120
}
121+
122+
export const loadFont = async (input: string) =>
123+
new Promise(async (resolve, reject) => {
124+
const url = new URL(input);
125+
const output = path.join(AWS_FONT_DIR, url.pathname.split('/').pop()!);
126+
if (await promisify(fs.exists)(output)) {
127+
resolve();
128+
return;
129+
}
130+
if (!fs.existsSync(AWS_FONT_DIR)) {
131+
await fsPromises.mkdir(AWS_FONT_DIR);
132+
}
133+
const stream = fs.createWriteStream(output);
134+
stream.once('error', (error) => {
135+
return reject(error);
136+
});
137+
https.get(input, (response) => {
138+
response.on('data', (chunk) => {
139+
stream.write(chunk);
140+
});
141+
142+
response.once('end', () => {
143+
stream.end(() => {
144+
return resolve();
145+
});
146+
});
147+
});
148+
});

src/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
import setEnvironmentVariables from './util/setEnvironmentVariables';
2-
3-
setEnvironmentVariables();
4-
51
export * from './chromium';

0 commit comments

Comments
 (0)