Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
62 changes: 62 additions & 0 deletions docs/docs/testing/intersection-observer-mock.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,65 @@ observer.simulateIntersection([
},
]);
```

### Typical unit-test

Here is an example of unit test with `IntersectionObserverMock`. For implementation `useIntersection` is used:

```tsx
import { IntersectionObserverContext, useIntersection } from '@krutoo/utils/react';
import { IntersectionObserverMock } from '@krutoo/utils/testing';
import { act, render } from '@testing-library/react';
import { describe, expect, mock, test } from '#test-utils';

function MyComponent({ onChange }) {
const ref = useRef(null);

useIntersection(ref, () => {
onChange?.();
});

return (
<div ref={ref} data-testid='my-component'>
Hello from my component
</div>
);
}

describe('MyComponent', () => {
test('should call onChange on intersection change', () => {
const observer = new ResizeObserverMock(() => {});

const context = {
getObserver(callback: IntersectionObserverCallback) {
return observer.addCallback(callback);
},
};

const spy = mock.fn();

const { getByTestId } = render(
<IntersectionObserverContext value={context}>
<MyComponent onChange={spy} />
</IntersectionObserverContext>,
);

// no calls after render
expect(spy.mock.callCount()).toBe(0);

// one call after intersection simulated
act(() => {
observer.simulateIntersection([
{
target: getByTestId('my-component'),
isIntersecting: true,
},
]);
});
expect(spy.mock.callCount()).toBe(1);
});
});
```

As you can see, mock includes chainable `addCallback` method.
You can use it to provide one observer for multiple components during test.
61 changes: 61 additions & 0 deletions docs/docs/testing/resize-observer-mock.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,64 @@ observer.simulateResize([
},
]);
```

### Typical unit-test

Here is an example of unit test with `ResizeObserverMock`. For implementation `useResize` is used:

```tsx
import { ResizeObserverContext, useResize } from '@krutoo/utils/react';
import { ResizeObserverMock } from '@krutoo/utils/testing';
import { act, render } from '@testing-library/react';
import { describe, expect, mock, test } from '#test-utils';

function MyComponent({ onResize }) {
const ref = useRef(null);

useResize(ref, () => {
onResize?.();
});

return (
<div ref={ref} data-testid='my-component'>
Hello from my component
</div>
);
}

describe('MyComponent', () => {
test('should call onResize on resize', () => {
const observer = new ResizeObserverMock(() => {});

const context = {
getObserver(callback: ResizeObserverCallback) {
return observer.addCallback(callback);
},
};

const spy = mock.fn();

const { getByTestId } = render(
<ResizeObserverContext value={context}>
<MyComponent onResize={spy} />
</ResizeObserverContext>,
);

// no calls after render
expect(spy.mock.callCount()).toBe(0);

// one call after resize simulated
act(() => {
observer.simulateResize([
{
target: getByTestId('my-component'),
},
]);
});
expect(spy.mock.callCount()).toBe(1);
});
});
```

As you can see, mock includes chainable `addCallback` method.
You can use it to provide one observer for multiple components during test.
20 changes: 16 additions & 4 deletions src/testing/intersection-observer-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export class IntersectionObserverMock implements IntersectionObserver {
/** @inheritdoc */
readonly thresholds: ReadonlyArray<number>;

/** Callback. */
protected readonly callback: IntersectionObserverCallback;
/** Callbacks. */
protected readonly callbacks: Set<IntersectionObserverCallback>;

/** Observed elements. */
protected readonly elements: Set<Element>;
Expand All @@ -28,7 +28,7 @@ export class IntersectionObserverMock implements IntersectionObserver {
this.rootMargin = init?.rootMargin ?? '0px 0px 0px 0px';
this.thresholds = Array.isArray(init?.threshold) ? init.threshold : [init?.threshold ?? 0];

this.callback = callback;
this.callbacks = new Set([callback]);
this.elements = new Set();
}

Expand Down Expand Up @@ -82,6 +82,18 @@ export class IntersectionObserverMock implements IntersectionObserver {
});
}

this.callback(readyEntries, this);
this.callbacks.forEach(fn => fn(readyEntries, this));
}

/**
* Adds callback.
* Useful to define single observer during tests and add callbacks to it.
* @param callback Callback.
* @returns This instance.
*/
addCallback(callback: IntersectionObserverCallback): this {
this.callbacks.add(callback);

return this;
}
}
20 changes: 16 additions & 4 deletions src/testing/resize-observer-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ export type PartialResizeObserverEntry = Pick<ResizeObserverEntry, 'target'> &
* Useful for unit testing.
*/
export class ResizeObserverMock implements ResizeObserver {
/** Callback. */
protected readonly callback: ResizeObserverCallback;
/** Callbacks. */
protected readonly callbacks: Set<ResizeObserverCallback>;

/** Observed elements. */
protected readonly elements: Set<Element>;

constructor(callback: ResizeObserverCallback) {
this.callback = callback;
this.callbacks = new Set([callback]);
this.elements = new Set();
}

Expand Down Expand Up @@ -60,6 +60,18 @@ export class ResizeObserverMock implements ResizeObserver {
});
}

this.callback(readyEntries, this);
this.callbacks.forEach(fn => fn(readyEntries, this));
}

/**
* Adds callback.
* Useful to define single observer during tests and add callbacks to it.
* @param callback Callback.
* @returns This instance.
*/
addCallback(callback: ResizeObserverCallback): this {
this.callbacks.add(callback);

return this;
}
}
Loading