Skip to content

Commit

Permalink
Merge 0f133d9 into e66874a
Browse files Browse the repository at this point in the history
  • Loading branch information
jtenner committed May 6, 2019
2 parents e66874a + 0f133d9 commit 1f178f4
Show file tree
Hide file tree
Showing 17 changed files with 5,124 additions and 101 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -6,5 +6,6 @@ node_js:
- "9"
- "10"
- "11"
- "12"
after_success:
- npm run coveralls
26 changes: 25 additions & 1 deletion CHANGELOG.md
@@ -1,10 +1,34 @@
# CHANGELOG

# Version 2.1.0

This minor version bump now has some major snapshot support but is backwards compatible.

Changes:

- Feature: Add `static` `CanvasRenderingContext2D` method: `#.__getEvents(ctx)`
- Feature: Every successful modification of the `CanvasRenderingContext2D` state machine logs an `_event`
- Feature: Add `static` `CanvasRenderingContext2D` method: `#.__getPath(ctx)`
- Feature: Every path call adds a `_path` item and can be accessed via `__getPath(ctx)`
- Feature: `beginPath()` empties the `_path`
- Feature: Add `static` `CanvasRenderingContext2D` method: `#.__getDrawCalls(ctx)`
- Feature: Every draw call adds a `_drawCall` item and can be accessed via `__getDrawCall(ctx)`
- Feature: Add `types/index.d.ts` file for tooling types (in jest environment)
- Feature: Support node 12
- Docs
- Updated arc example
- Added snapshot testing documentation
- Bug: `createLinearGradient` now accepts strings
- Bug: `createRadialGradient` now accepts strings
- Bug: `globalAlpha` now accepts `null` per `Number` coercion
- Feature: Faster finite values checks
- Feature: Add `_path` and `_events` to `Path2D`
- Testing: Add and test snapshot outputs

# Version 2.0.0

Just publish a stable version.


# Version 2.0.0-beta.1

## Class Instances
Expand Down
59 changes: 53 additions & 6 deletions README.md
Expand Up @@ -73,8 +73,11 @@ error. For instance, the `CanvasRenderingContext2D#arc` function will throw a `T
radius is negative, or if it was not provided with enough parameters.

```ts
// arc throws a TypeError when the argument length is less than 5
expect(() => ctx.arc(1, 2, 3, 4)).toThrow(TypeError);
expect(() => ctx.arc(0, 0, -10, 0, Math.PI * 2)).toThrow(TypeError);

// when radius is negative, arc throws a dom exception when all parameters are finite
expect(() => ctx.arc(0, 0, -10, 0, Math.PI * 2)).toThrow(DOMException);
```

The function will do `Number` type coercion and verify the inputs exactly like the browser does. So
Expand All @@ -95,16 +98,60 @@ expect(() => ctx.fill(new Path2D(), "invalid!")).toThrow(TypeError);

We try to follow the ECMAScript specification as closely as possible.

# Snapshots

There are multiple ways to validate canvas state. There are currently three `static` methods attached
to the `CanvasRenderingContext2D` class. The first way to use this feature is by using the `__getEvents`
method.

```ts
/**
* In order to see which functions and properties were used for the test, you can use `__getEvents`
* to gather this information.
*/
const events = ctx.__getEvents();

expect(events).toMatchSnapshot(); // jest will assert the events match the snapshot
```

The second way is to inspect the current path associated with the context.

```ts
ctx.beginPath();
ctx.arc(1, 2, 3, 4, 5);
ctx.moveTo(6, 7);
ctx.rect(6, 7, 8, 9);
ctx.closePath();

/**
* Any method that modifies the current path (and subpath) will be pushed to an event array. When
* using the `__getPath` method, that array will sliced and usable for snapshots.
*/
const path = ctx.__getPath();
expect(path).toMatchSnapshot();
```

The third way is to inspect all of the success draw calls submitted to the context.

```ts
ctx.drawImage(img, 0, 0);

/**
* Every drawImage, fill, stroke, fillText, or strokeText function call will be logged in an event
* array. This method will return those events here for inspection.
*/
const calls = ctx.__getDrawCalls();
expect(calls).toMatchSnapshot();
```

## Override default mock return value

You can override the default mock return value in your test to suit your need. For example, to override return value of `toDataURL`:

```ts
HTMLCanvasElement.prototype.toDataURL = jest
.fn()
.mockReturnValue(
'data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
);
canvas.toDataURL.mockReturnValueOnce(
'data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
);
```

## License
Expand Down
131 changes: 131 additions & 0 deletions __tests__/classes/CanvasRenderingContext2D.__getDrawCalls.js
@@ -0,0 +1,131 @@
let ctx;
beforeEach(() => {
// get a new context each test
ctx = document.createElement('canvas')
.getContext('2d');
});

const img = new Image();
img.src = 'https://placekitten.com/400/300';
img.width = 400;
img.height = 300;

const path = new Path2D();
path.arc(100, 101, 10, 0, Math.PI * 2);

afterEach(() => {
const drawCalls = ctx.__getDrawCalls(ctx);
expect(drawCalls).toMatchSnapshot();
});

describe('__getDrawCalls', () => {
it('should have a draw call when clearRect is called', () => {
ctx.clearRect(1, 2, 3, 4);
});

it('should not have a draw call when clearRect is passed bad values', () => {
ctx.clearRect(NaN, 1, 2, 3);
});

it('should have a draw call when fillRect is called', () => {
ctx.fillRect(1, 2, 3, 4);
});

it('should not have a draw call when fillRect is passed bad values', () => {
ctx.fillRect(NaN, 1, 2, 3);
});

it('should have a draw call when strokeRect is called', () => {
ctx.strokeRect(1, 2, 3, 4);
});

it('should not have a draw call when strokeRect is passed bad values', () => {
ctx.strokeRect(NaN, 1, 2, 3);
});

it('should have a draw call when drawImage is called', () => {
ctx.drawImage(img, 0, 0);
});

it('should not have a draw call when drawImage is called with non-finite numbers', () => {
ctx.drawImage(img, NaN, 0);
});

it('should have a draw call when drawImage is called with size parameters', () => {
ctx.drawImage(img, 0, 0, 100, 100);
});

it('should not have a draw call when drawImage is called with non-finite numbers and size parameters', () => {
ctx.drawImage(img, NaN, 0, 100, 100);
});

it('should have a draw call when drawImage is called with source parameters', () => {
ctx.drawImage(img, 0, 0, 100, 100, 0, 0, 200, 200);
});

it('should not have a draw call when drawImage is called with non-finite numbers and source parameters', () => {
ctx.drawImage(img, NaN, 0, 100, 100, 0, 0, 200, 200);
});

it('should have a draw call when fill() is called', () => {
ctx.beginPath();
ctx.arc(100, 101, 10, 0, Math.PI * 2);
ctx.fill();
});

it('should have a draw call when fill() is called with a fillRule', () => {
ctx.beginPath();
ctx.arc(100, 101, 10, 0, Math.PI * 2);
ctx.fill('evenodd');
});

it('should have a draw call when using a path', () => {
ctx.fill(path);
});

it('should have a draw call when fillRule is valid', () => {
ctx.fill(path, 'evenodd');
});

it('should have a draw call when fillText is valid', () => {
ctx.fillText('hello world', 0, 0);
});

it('should have a draw call when fillText has valid maxWidth', () => {
ctx.fillText('hello world', 0, 0, 100);
});

it('should not have a draw call when fillText is not valid', () => {
ctx.fillText('hello world', 0, NaN);
});

it('should not have a draw call when fillText is not valid', () => {
ctx.fillText('hello world', 0, 0, NaN);
});

it('should have a draw call when strokeText is valid', () => {
ctx.strokeText('hello world', 0, 0);
});

it('should have a draw call when strokeText has valid maxWidth', () => {
ctx.strokeText('hello world', 0, 0, 100);
});

it('should not have a draw call when strokeText is not valid', () => {
ctx.strokeText('hello world', 0, NaN);
});

it('should not have a draw call when strokeText is not valid', () => {
ctx.strokeText('hello world', 0, 0, NaN);
});

it('should have a draw call when stroke is called', () => {
ctx.beginPath();
ctx.arc(100, 101, 10, 0, Math.PI * 2);
ctx.stroke();
});

it('should have a draw call when stroke is called with a path', () => {
ctx.stroke(path);
});
});

0 comments on commit 1f178f4

Please sign in to comment.