Skip to content

Commit

Permalink
feat: setup puppeteer
Browse files Browse the repository at this point in the history
  • Loading branch information
alexarassat committed Sep 24, 2024
1 parent 4bd864a commit c6127cb
Show file tree
Hide file tree
Showing 7 changed files with 1,896 additions and 21 deletions.
2 changes: 1 addition & 1 deletion flowplayer.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FlowplayerUMD} from "@flowplayer/player"
import type { FlowplayerUMD} from "@flowplayer/player"
declare global {
var flowplayer: FlowplayerUMD;
}
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"scripts": {
"new:component": "tsx tooling/code-gen/bin.ts",
"cdn:manifest": "tsx tooling/cdn/bin.ts",
"build": "webpack"
"build": "webpack",
"test:puppeteer": "yarn ava --config test/puppeteer/_ava.config.mjs"
},
"devDependencies": {
"@types/inquirer": "^9.0.7",
Expand All @@ -19,6 +20,13 @@
"typescript": "latest",
"webpack": "^5.91.0",
"ts-loader": "^9.5.1",
"puppeteer": "^14.4.0",
"mkdirp": "^1.0.4",
"http-server": "^14.1.0",
"puppeteer-screen-recorder": "^2.0.2",
"ava": "^4.2.0",
"pug": "^3.0.3",
"dotenv": "^16.0.1",
"style-loader": "^4.0.0",
"css-loader": "^7.1.2",
"webpack-cli": "^5.1.4"
Expand Down
6 changes: 6 additions & 0 deletions test/puppeteer/_ava.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
files: ["./test/puppeteer/**/*.spec.mjs"],
require: ["./test/puppeteer/_setup/helpers.mjs"],
timeout: "5m",
concurrency: 3
}
197 changes: 197 additions & 0 deletions test/puppeteer/_setup/helpers.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import pug from "pug"
import os, {tmpdir} from "os"
import fs from "fs/promises"
import path from "path"
import puppeteer from "puppeteer"
import HttpServer from "http-server"
import "dotenv/config"
import mkdirp from "mkdirp"
import {PuppeteerScreenRecorder} from "puppeteer-screen-recorder"

const debug = (...args) => process.env.DEBUG && console.log(...args)
const _dirname = path.dirname(import.meta.url)
const tmpDir = path.join(os.tmpdir(), "puppeteer-native", "html")

export function compilePug(name) {
const fileName = path.join(_dirname, name + ".pug").slice(5)
return pug.compileFile(fileName, {})
}

const Templates = {local: compilePug("local")}

export async function createServer(t) {
return new Promise((resolve) => {
const server = HttpServer.createServer({root: tmpDir})
server.listen(0, "localhost", () => {
Object.assign(t.context, {server, ...server.server.address()})
Object.assign(t.context, {address: "localhost"})
debug("server / up / %s:%s", t.context.address, t.context.port)
resolve()
})
})
}

export async function destroyServer(t) {
if (!t.context.server) return
t.context.server.close()
t.context.server = void 0
}

export async function loadComponent(componentName) {
const component = await fs.readFile(
path.join("dist", `${componentName}.js`)
)

return component.toString()
}

export async function writeTempFile(fileName, contents) {
const fullName = path.join(tmpDir, fileName)
debug("created: %s", fullName)
await fs.writeFile(fullName, contents)
return fullName
}

export function titleToFile(title, suffix = ".html") {
const fileName = title
.toLowerCase()
.replaceAll(/\s+/g, "-")
.replaceAll(/[/]/g, "-")
.replaceAll(/,/g, "-")
.replaceAll(/-{2,}/g, "-")
.replaceAll(/=/g, "-")
.replaceAll(/_/g, "-")
return fileName + suffix
}

export async function compileLocalTest(t, {config, components: componentNames}) {
const components = await Promise.all(componentNames.map((name) => loadComponent(name)))

const compiled = Templates.local({
Test: {
components,
config: config
}
})
const fileName = titleToFile(t.title)
const filePath = await writeTempFile(fileName, compiled)
return {fileName, filePath}
}

const {
PUPPETEER_PRODUCT: puppeteerProduct,
CHROME_PATH: chromePath = "/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome",
FIREFOX_PATH: firefoxPath = "/usr/bin/firefox"
} = process.env

const headless = !!process.env.CI || !!process.env.PUPPETEER_HEADLESS

const executablePath = puppeteerProduct === "firefox" ? firefoxPath : chromePath

export async function makePuppeteerSession(t, args) {
args = args || []
if (process.env.CI) args.push("--no-sandbox")

const browser = await puppeteer.launch({
headless,
executablePath,
slowMo: !headless ? 50 : 200,
userDataDir: path.join(tmpdir(), `puppeteer-ci-${Date.now()}`),
args
})
const page = await browser.newPage({timeout: 120 * 1000})
const recorder = new PuppeteerScreenRecorder(page)
page.on("console", (msg) => {
if (!process.env.DEBUG) return
for (let i = 0; i < msg.args().length; ++i)
debug(`console / ${t.title} / ${i}: ${msg.args()[i]}`)
})
return {browser, page, recorder}
}

async function getPlayer(
t,
components,
config,
token,
puppeteer,
recorder,
setup,
host,
page
) {
const context = {host, page}
if (typeof setup == "function") {
await setup(context)
}
const recordingFile =
"/tmp/puppeteer-native/components/recordings/" + titleToFile(t.title, ".mp4")

if (!process.env.DEBUG) await recorder.start(recordingFile)
const {fileName, filePath} = await compileLocalTest(t, {
config: JSON.stringify(config).replaceAll(":host:", host),
components,
})
const testDocument = `${context.host}/${fileName}` //host + fileName
await page.goto(testDocument, puppeteer || {waitUntil: "networkidle2", timeout: 120 * 1000}
)
const player = await page.$(".fp-engine")
return {player, filePath, recordingFile}
}

export function withPlayer({components, config, token, files, puppeteer, args}, setup) {
return async function (t, run) {
await mkdirp(tmpDir)
await mkdirp("/tmp/puppeteer-native/components/recordings/")
if (!t.context.address) await createServer(t)
const host = `http://localhost:${t.context.port}`
if (files)
await Promise.all(
Object.entries(files).map(([fileName, contents]) =>
writeTempFile(fileName, contents)
)
)

const {browser, page, recorder} = await makePuppeteerSession(t, args)

const {player, filePath, recordingFile} = await getPlayer(
t,
components,
config,
token,
puppeteer,
recorder,
setup,
host,
page
)

t.assert(player)
try {
try {
await run(t, page, player)
} catch (err) {
// todo: open an issue with ava.js about this
t.fail(err.message)
}
} finally {
await destroyServer(t)
if (!process.env.DEBUG) await recorder.stop()
if (t.passed) {
const testFiles = Object.keys(files || {}).map((fileName) =>
path.join(tmpDir, fileName)
)
const rm = async (f) => {
if (process.env.KEEP) return
if (process.env.DEBUG) debug("cleaning up file %s", f)
if (await fs.stat(f)) {
return await fs.rm(f)
}
}
const removals = [rm(recordingFile), rm(filePath), ...testFiles.map(rm)]
await Promise.all(removals)
}
if (!process.env.KEEP) await browser.close()
}
}
}
22 changes: 22 additions & 0 deletions test/puppeteer/_setup/local.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
html(lang="en")
meta(charset="utf-8")
head
link(rel="stylesheet" href="https://cdn.flowplayer.com/releases/native/3/stable/style/flowplayer.css")
script(src = "https://cdn.flowplayer.com/releases/native/3/stable/flowplayer.min.js")
script(src = "https://cdn.flowplayer.com/releases/native/3/stable/plugins/hls.min.js")
script(src = "https://cdn.flowplayer.com/releases/native/3/stable/plugins/ovp.min.js")
script(src = "https://cdn.flowplayer.com/releases/native/3/stable/plugins/qsel.min.js")
script(src = "https://cdn.flowplayer.com/releases/native/3/stable/plugins/speed.min.js")
script(src = "https://cdn.flowplayer.com/releases/native/3/stable/plugins/vtsel.min.js")
body
div(id = "player")
//inject components
each component in Test.components
script!= component

script
| localStorage["flowplayer/debug"] = ".*"
| window.Errors = []
| window.onerror = function(messageOrEvent, source, lineno, colno, error) { window.Errors.push([...arguments]) }
| window.__FLOWPLAYER_TOKEN = "eyJraWQiOiJiRDJrSnhuTkppT1AiLCJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJjIjoie1wiYWNsXCI6NixcImlkXCI6XCJiRDJrSnhuTkppT1BcIn0iLCJpc3MiOiJGbG93cGxheWVyIn0.6SS-jLJb338KVYAsj4SFVBzbah-auDeQjeBqjJL6SRa_vJKt4xW7-lSlZGjqsKAqhXFso2UF_BaBkJZ1S1SQFg"
| window.player = flowplayer("#player", !{Test.config})
16 changes: 16 additions & 0 deletions test/puppeteer/tests/first-test.spec.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import test from "ava"
import {createServer, destroyServer, withPlayer} from "../_setup/helpers.mjs"

test.before("setting up server...", createServer)
test.after("shutting down server...", destroyServer)


const Player = withPlayer({
components: ["combined-menu-control"], config: {
src: "https://fp-eu-w1-nnarhinen.s3.eu-west-1.amazonaws.com/multi-angle-hls/playlist.m3u8"
}
})

test("dummy test", Player, async (t, page, player) => {
setTimeout(() => console.log(player), 20_000)
})
Loading

0 comments on commit c6127cb

Please sign in to comment.