Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent regressions when upgrading pdfjs-dist #937

Merged
merged 19 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
4b94f1e
:rewind: revert: Bump pdfjs-dist to v3.11.174 explicitly.
make-github-pseudonymous-again May 13, 2024
48d067a
:rewind: revert: Upgrade dependency pdfjs-dist to ~3.11.0.
make-github-pseudonymous-again May 13, 2024
7ab87e5
:gear: config(ci): Always run NPM postinstall hook in workflows.
make-github-pseudonymous-again May 12, 2024
861cf94
:umbrella: test(pdf): Prevent regression when upgrading pdfjs-dist.
make-github-pseudonymous-again May 11, 2024
99f4c4b
:woman_technologist: dx: Fix warnings caused by example PDF used in t…
make-github-pseudonymous-again May 13, 2024
d4f5cc2
:woman_technologist: dx(pdf): Disable functions that are not isomorph…
make-github-pseudonymous-again May 13, 2024
4eff7d4
:woman_technologist: dx: Facilitate writing isomorphic tests.
make-github-pseudonymous-again May 13, 2024
f12cdaa
:recycle: refactor: Factor out data URL constructor.
make-github-pseudonymous-again May 13, 2024
cf40e07
:woman_technologist: dx(server): Add modern `fetch` polyfill.
make-github-pseudonymous-again May 13, 2024
5f1d7f5
:woman_technologist: dx: Define Blob from/to DataURL conversion utili…
make-github-pseudonymous-again May 13, 2024
12d32d7
:test_tube: test(pdf): Test thumbnail-generating functions.
make-github-pseudonymous-again May 13, 2024
4fb14f2
:woman_technologist: dx: Factor out PDF example from tests.
make-github-pseudonymous-again May 13, 2024
97ade6e
:recycle: refactor(tests): Simplify creation of PDF sample data.
make-github-pseudonymous-again May 13, 2024
17b96b6
:woman_technologist: dx(tests): Simplify server polyfill setup.
make-github-pseudonymous-again May 13, 2024
3f9bed1
:recycle: refactor(client): Abstract away polyfills.
make-github-pseudonymous-again May 13, 2024
a792043
:woman_technologist: dx(tests): Use DRY client polyfill.
make-github-pseudonymous-again May 13, 2024
cc387dd
:recycle: refactor(lib/pdf): Factor out isomorphic canvas helpers.
make-github-pseudonymous-again May 13, 2024
fd19715
:test_tube: test(lib/pdf): Check thumbnail helper functions outputs.
make-github-pseudonymous-again May 13, 2024
02e9cba
:microscope: test: Increase coverage target to 67%.
make-github-pseudonymous-again May 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/actions/install/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: install
description: >
Installs Meteor and NPM dependencies.

runs:

using: composite

steps:

- name: Install 💾
id: install
uses: meteor-actions/install@v6

- name: Postinstall 🪝
if: steps.install.outputs.ran-npm-install-hooks != 'true'
shell: bash
run: meteor npm run postinstall
2 changes: 1 addition & 1 deletion .github/workflows/ci:build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
uses: actions/checkout@v4

- name: Install 💾
uses: meteor-actions/install@v6
uses: ./.github/actions/install

- name: Get Meteor's Node version
id: meteor-node
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci:commit-msg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
fetch-depth: 0

- name: Install 💾
uses: meteor-actions/install@v6
uses: ./.github/actions/install

- name: Lint last pushed commit 👕
if: github.event_name == 'push'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci:lint-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: actions/checkout@v4

- name: Install 💾
uses: meteor-actions/install@v6
uses: ./.github/actions/install

- name: Lint config 👕
run: meteor npm run lint-config
2 changes: 1 addition & 1 deletion .github/workflows/ci:lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: actions/checkout@v4

- name: Install 💾
uses: meteor-actions/install@v6
uses: ./.github/actions/install

- name: Lint 👕
run: meteor npm run lint
4 changes: 2 additions & 2 deletions .github/workflows/ci:test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}

- name: Install 💾
uses: meteor-actions/install@v6
uses: ./.github/actions/install

- name: Cache build 💽
uses: meteor-actions/cache-build@v4
Expand Down Expand Up @@ -92,7 +92,7 @@ jobs:
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}

- name: Install 💾
uses: meteor-actions/install@v6
uses: ./.github/actions/install

- name: Cache build 💽
uses: meteor-actions/cache-build@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci:type-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: actions/checkout@v4

- name: Install 💾
uses: meteor-actions/install@v6
uses: ./.github/actions/install

- name: TypeScript check ☑️
run: meteor npm run tsc
5 changes: 3 additions & 2 deletions client/main.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
// eslint-disable-next-line import/order, import/no-unassigned-import
import './polyfill';

import React, {StrictMode} from 'react';

import {Meteor} from 'meteor/meteor';
Expand Down
2 changes: 2 additions & 0 deletions client/polyfill.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ coverage:
status:
project:
default:
target: 66%
target: 67%
threshold: 2%
patch:
default:
Expand Down
9 changes: 9 additions & 0 deletions imports/_test/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@
let original;

const prepare = async () => {
await import('../../client/polyfill');

original = await forgetHistory();
await cleanup();
};
Expand All @@ -113,6 +115,8 @@
};

const prepare = async () => {
await import('../../server/polyfill');

if (isAppTest()) {
await appIsReady();
}
Expand All @@ -132,6 +136,11 @@
}
};

export const isomorphic = (title, fn) => {

Check warning on line 139 in imports/_test/fixtures.ts

View check run for this annotation

Codecov / codecov/patch

imports/_test/fixtures.ts#L139

Added line #L139 was not covered by tests
client(title, fn);
server(title, fn);
};

export const throws = async (
fn: () => Promise<any>,
expected: string | RegExp,
Expand Down
164 changes: 164 additions & 0 deletions imports/_test/image.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import {assert} from 'chai';

import {type Sharp} from 'sharp';

import {createContextIso, destroyContextIso} from '../lib/canvas';
import blobToImage from '../lib/blob/blobToImage';

type TypedArray =
| Uint8Array
| Uint8ClampedArray
| Int8Array
| Uint16Array
| Int16Array
| Uint32Array
| Int32Array
| Float32Array
| Float64Array;
type SharpInput = ArrayBuffer | TypedArray;
type S = Sharp | SharpInput;

type I = HTMLImageElement | ArrayBuffer;

type Image = S | I;

const _s = async (x: S): Promise<Sharp> => {
const {default: sharp} = await import('sharp');
return x instanceof sharp ? (x as Sharp) : sharp(x as SharpInput);
};

const _i = async (x: I): Promise<HTMLImageElement> => {
if (x instanceof HTMLImageElement) return x;
const blob = new Blob([x], {type: 'image/png'});
const image = await blobToImage(blob);
return image;
};

const _pixels = async (image: HTMLImageElement): Promise<Uint8ClampedArray> => {
const {width, height} = image;
const context = await createContextIso({width, height});
context.drawImage(image, 0, 0);
const data = context.getImageData(0, 0, width, height).data;
destroyContextIso(context);
return data;
};

const _xorClient = (
a: Uint8ClampedArray,
b: Uint8ClampedArray,
): Uint8ClampedArray => {
const n = a.length;
assert.equal(b.length, n);
const delta = new Uint8ClampedArray(n);
for (let i = 0; i < n; ++i) {
// eslint-disable-next-line no-bitwise
delta[i] = a[i]! ^ b[i]!;
}

return delta;
};

const _diffClient = async (a: Image, b: Image) => {
const _a = await _i(a as I);
const _b = await _i(b as I);
const aPixels = await _pixels(_a);
const bPixels = await _pixels(_b);
return _xorClient(aPixels, bPixels);
};

const _diffServer = async (a: Image, b: Image) => {
const _a = await _s(a as S);
const _b = await _s(b as S);
const delta = await _xorServer(_a, _b);
return delta.raw().toBuffer();
};

export const diff = Meteor.isServer ? _diffServer : _diffClient;

export const assertEqual = async (a: Image, b: Image) => {
const delta = await diff(a, b);
assert(
!delta.some((value, index) => index % 4 !== 3 && value !== 0),
`Input images are not equal.`,
);
};

const _assertSameDimensions = async (a: Sharp, b: Sharp): Promise<void> => {
const {
width: aWidth,
height: aHeight,
channels: aChannels,
hasAlpha: aHasAlpha,
} = await a.metadata();
const {
width: bWidth,
height: bHeight,
channels: bChannels,
hasAlpha: bHasAlpha,
} = await b.metadata();

assert.equal(
aWidth,
bWidth,
`Images have different widths: ${aWidth} !== ${bWidth}`,
);

assert.equal(
aHeight,
bHeight,
`Images have different heights: ${aHeight} !== ${bHeight}`,
);

assert.equal(
aChannels,
bChannels,
`Images have different number of channels: ${aChannels} !== ${bChannels}`,
);

assert.equal(
aHasAlpha,
bHasAlpha,
`Images have different alpha channel settings: ${aHasAlpha} !== ${bHasAlpha}`,
);
};

const _xorServer = async (a: Sharp, b: Sharp): Promise<Sharp> => {
await _assertSameDimensions(a, b);
const _b = await b.toBuffer();
return a.boolean(_b, 'eor');
};

const _whiteRectanglePNGServer = async (options: {
width: number;
height: number;
}) => {
const {default: sharp} = await import('sharp');
return sharp({
create: {
...options,
channels: 4,
background: {r: 255, g: 255, b: 255, alpha: 0},
},
}).png();
};

const _whiteRectanglePNGClient = async ({
width,
height,
}: {
width: number;
height: number;
}): Promise<HTMLImageElement> => {
const context = await createContextIso({width, height});
context.fillStyle = '#FFFFFF';
context.fillRect(0, 0, width, height);
const url = context.canvas.toDataURL('image/png');
destroyContextIso(context);
const img = new Image();
img.src = url;
return img;
};

export const whiteRectanglePNG = Meteor.isServer
? _whiteRectanglePNGServer
: _whiteRectanglePNGClient;
31 changes: 31 additions & 0 deletions imports/_test/pdf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import blobToDataURL from '../lib/blob/blobToDataURL';

const document = `%PDF-1.0
1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj
2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj
3 0 obj<</Type/Page/MediaBox[0 0 3 3]>>endobj
xref
0 4
0000000000 65535 f
0000000009 00000 n
0000000052 00000 n
0000000101 00000 n
trailer<</Size 4/Root 1 0 R>>
startxref
147
%EOF
`;

export const randomPDFUint8Array = (): Uint8Array => {
return new TextEncoder().encode(document);
};

export const randomPDFBlob = (): Blob => {
const data = randomPDFUint8Array();
return new Blob([data.buffer], {type: 'application/pdf'});
};

export const randomPDFDataURI = async (): Promise<string> => {
const blob = randomPDFBlob();
return blobToDataURL(blob);
};
5 changes: 3 additions & 2 deletions imports/_test/png.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pngDataURL from '../lib/png/dataURL';

const base64Encoded =
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';

Expand All @@ -6,8 +8,7 @@ export const randomPNGBuffer = async () => {
return Buffer.from(base64Encoded, 'base64');
};

export const randomPNGDataURI = (): string =>
`data:image/png;base64,${base64Encoded}`;
export const randomPNGDataURI = (): string => pngDataURL(base64Encoded);

export const randomPNGResponse = async (): Promise<Response> =>
fetch(randomPNGDataURI());
Expand Down
2 changes: 0 additions & 2 deletions imports/api/endpoint/allergies/changeColor.tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import {Allergies} from '../../collection/allergies';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import {
Expand Down
2 changes: 0 additions & 2 deletions imports/api/endpoint/appointments/cancel.tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import {
Expand Down
2 changes: 0 additions & 2 deletions imports/api/endpoint/appointments/remove.tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import {
Expand Down
2 changes: 0 additions & 2 deletions imports/api/endpoint/appointments/reschedule.tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import {Appointments} from '../../collection/appointments';
Expand Down
2 changes: 0 additions & 2 deletions imports/api/endpoint/appointments/schedule.tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import {
Expand Down
2 changes: 0 additions & 2 deletions imports/api/endpoint/appointments/uncancel.tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import {
Expand Down
2 changes: 0 additions & 2 deletions imports/api/endpoint/availability/next.tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import addMinutes from 'date-fns/addMinutes';
Expand Down
2 changes: 0 additions & 2 deletions imports/api/endpoint/books/csv.tests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line import/no-unassigned-import
import 'regenerator-runtime/runtime.js';
import {assert} from 'chai';

import startOfYear from 'date-fns/startOfYear';
Expand Down
Loading
Loading