Event tracking has always been a pain point in the development and testing process of Bilibili's HTML5 player. There are many types of event tracking and interface parameters, which makes testing difficult and prone to errors.
Although testing is troublesome, the rules for event tracking are simple. For example, clicking or hovering over a button, error reporting, and reporting playback and performance. So, can we replace these tedious, mechanized, and error-prone testing tasks with automated end-to-end (E2E) testing?
After a tracking incident, I spent a day exploring and the results were good. Here is a simple summary.
Writing Test Scripts#
To simulate user operations, we need to use a headless browser. I used the combination of Jest and Puppeteer.
Jest is a testing framework, and Puppeteer is used to control Chrome or Chromium.
I chose Jest because I am most familiar with it, and then I found a preset: jest-puppeteer. It is not necessary, but it can simplify many Puppeteer operations.
Install dependencies:
npm install --save-dev jest jest-puppeteer puppeteer
The test script is very simple:
describe('log', () => {
beforeAll(async () => {
page.goto('https://www.bilibili.com/video/av44890855');
});
it('play_screen', async (done) => {
page.on('request', (request) => {
if (request.url().match(/^https:\/\/data\.bilibili\.com\/log\/web\?play_screen...parameters/)) {
done();
}
});
page.click('video');
});
});
Open a playback page with HeadlessChrome, monitor the interface requests of the page, simulate clicking on the video element, and verify that the play_screen event tracking is successful.
It seems that there is no problem, so I happily executed the test, but it failed.
What happened? I configured headless: false
and watched the test process.
I found that it failed because the browser detected that HTML5 player is not supported and loaded the Flash player.
According to the Puppeteer documentation:
Puppeteer is bundled with Chromium--not Chrome...Puppeteer does not support licensed formats such as AAC or H.264
The solution is simple, replace the Puppeteer's bundled Chromium with the local Chrome.
launch: {
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
}
The test passed.
Chrome as a Service#
Using the local Chrome relies on the local environment, and it is not feasible to run it as an automated test on a test machine.
So, I ran a Docker container on the test machine: browserless/chrome. It treats Chrome as a service, and the test script uses the WebSocket protocol to operate the Chrome inside the Docker container, avoiding the dependency on the local Chrome.
Start the container:
docker pull browserless/chrome:release-chrome-stable
docker run -d -p 3000:3000 browserless/chrome:release-chrome-stable
Use:
connect: {
browserWSEndpoint: 'ws://localhost:3000'
}
Intercepting JS#
Using the online version doesn't test the local code at all!
Oh, I forgot to mention that we also need to intercept the online JS and replace it with the local version.
await page.setRequestInterception(true);
page.goto('https://www.bilibili.com/video/av44890855');
page.on('request', (request) => {
if (request.url().match(/player\.js/)) {
request.respond({
status: 200,
contentType: 'application/javascript',
body: fs.readFileSync('dist/release/player.js').toString()
});
} else {
request.continue();
}
});