Repository URL to install this package:
|
Version:
1.0.1 ▾
|
/* eslint-env jest */
import 'jest'
import '../../polyfill/IntersectionObserver'
import * as React from 'react'
import { create } from 'react-test-renderer'
import IntersectionObserverComponent from '../IntersectionObserver'
import {
callback,
findObserverElement,
observerElementsMap
} from '../observer'
function mockUtilsFunctions() {
const utils = require.requireActual('../utils')
return {
...utils,
isDOMTypeElement() {
return true
},
}
}
jest.mock('../utils', () => mockUtilsFunctions())
const noop = () => {}
const target = { nodeType: 1 }
const propTypes = IntersectionObserverComponent.propTypes
beforeAll(() => {
IntersectionObserverComponent.propTypes = {}
})
afterAll(() => {
IntersectionObserverComponent.propTypes = propTypes
})
afterEach(() => {
observerElementsMap.clear()
})
test('throws when the property children is not an only child', () => {
global.spyOn(console, 'error')
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
<span />
</IntersectionObserverComponent>
)
expect(() => create(component)).toThrowErrorMatchingSnapshot()
})
test('throws on mount if children is StatelessComponent in React 15', () => {
global.spyOn(console, 'error')
const { version } = React
const StatelessComponent = () => <span />
const component = (
<IntersectionObserverComponent onChange={noop}>
<StatelessComponent />
</IntersectionObserverComponent>
)
React.version = '15.4.0'
expect(() => create(component)).toThrowErrorMatchingSnapshot()
React.version = version
})
test('should call ref callback of children', () => {
const spy = jest.fn()
const component = (
<IntersectionObserverComponent onChange={noop}>
<span ref={spy} />
</IntersectionObserverComponent>
)
create(component, { createNodeMock: () => target })
expect(spy).toHaveBeenCalledWith(target)
})
test('should handle children ref of type RefObject', () => {
const ref = React.createRef()
const component = (
<IntersectionObserverComponent onChange={noop}>
<span ref={ref} />
</IntersectionObserverComponent>
)
create(component, { createNodeMock: () => target })
expect(ref.current).toEqual(target)
})
test('options getter returns propTypes `root`, `rootMargin` and `threshold`', () => {
const options = {
root: { nodeType: 1 },
rootMargin: '50% 0%',
threshold: [0, 1],
}
const component = (
<IntersectionObserverComponent onChange={noop} {...options}>
<span />
</IntersectionObserverComponent>
)
const tree = create(component, { createNodeMock: () => target })
expect(tree.getInstance().options).toEqual(options)
})
test('should save target in the observer targets\' list on mount', () => {
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
const tree = create(component, { createNodeMock: () => target })
const observer = tree.getInstance().observer
const retrieved = findObserverElement(observer, { target })
expect(retrieved).toEqual(tree.getInstance())
})
test('should remove target from the observer targets\' list on umount', () => {
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
const tree = create(component, { createNodeMock: () => target })
const instance = tree.getInstance()
const observer = instance.observer
tree.unmount()
const retrieved = findObserverElement(observer, { target })
expect(retrieved).toBeNull()
})
describe('update', () => {
test('componentDidUpdate reobserves the target with observer prop changes', () => {
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
const tree = create(component, { createNodeMock: () => target })
const instance = tree.getInstance()
const spy1 = jest.spyOn(instance, 'observe')
const spy2 = jest.spyOn(instance, 'unobserve')
tree.update(
<IntersectionObserverComponent onChange={noop} rootMargin="20% 10%">
<span />
</IntersectionObserverComponent>
)
expect(spy1).toHaveBeenCalledTimes(1)
expect(spy2).toHaveBeenCalledTimes(1)
})
test('should cleanup when tree reconciliation has led to a full rebuild', () => {
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
let called = false
const tree = create(component, {
createNodeMock() {
if (called) {
return target
}
called = true
return Object.assign({ id: 2 }, target)
},
})
const instance = tree.getInstance()
const spy1 = jest.spyOn(instance, 'unobserve')
const spy2 = jest.spyOn(instance, 'observe')
tree.update(
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
tree.update(
<IntersectionObserverComponent onChange={noop}>
<div />
</IntersectionObserverComponent>
)
expect(spy1).toHaveBeenCalledTimes(1)
expect(spy2).toHaveBeenCalledTimes(1)
expect(instance.target).toBe(target)
})
test('should reobserve with new root, rootMargin and/or threshold props', () => {
const root1 = Object.assign({ id: 'window' }, target)
const root2 = Object.assign({ id: 'document' }, target)
const initialProps = {
onChange: noop,
root: root1,
rootMargin: '10% 20%',
threshold: 0.5,
}
const component = (
<IntersectionObserverComponent {...initialProps}>
<span />
</IntersectionObserverComponent>
)
const tree = create(component, { createNodeMock: () => target })
const instance = tree.getInstance()
const spy1 = jest.spyOn(instance, 'unobserve')
const spy2 = jest.spyOn(instance, 'observe')
// none of the props updating
tree.update(
<IntersectionObserverComponent {...initialProps}>
<span />
</IntersectionObserverComponent>
)
// only children updating
tree.update(
<IntersectionObserverComponent {...initialProps}>
<div />
</IntersectionObserverComponent>
)
// only root updating (document)
tree.update(
<IntersectionObserverComponent {...initialProps} root={root2}>
<div />
</IntersectionObserverComponent>
)
// only root updating (window)
tree.update(
<IntersectionObserverComponent {...initialProps} root={root1}>
<div />
</IntersectionObserverComponent>
)
// only rootMargin updating
tree.update(
<IntersectionObserverComponent {...initialProps} root={root1} rootMargin="20% 10%">
<div />
</IntersectionObserverComponent>
)
// only root updating (null)
tree.update(
<IntersectionObserverComponent {...initialProps} rootMargin="20% 10%">
<div />
</IntersectionObserverComponent>
)
// only threshold updating (non-scalar)
tree.update(
<IntersectionObserverComponent {...initialProps} threshold={[0.5, 1]}>
<div />
</IntersectionObserverComponent>
)
// only threshold updating (length changed)
tree.update(
<IntersectionObserverComponent
{...initialProps}
threshold={[0, 0.25, 0.5, 0.75, 1]}
>
<div />
</IntersectionObserverComponent>
)
// only threshold updating (scalar)
tree.update(
<IntersectionObserverComponent {...initialProps} threshold={1}>
<div />
</IntersectionObserverComponent>
)
expect(spy1).toHaveBeenCalledTimes(6)
expect(spy2).toHaveBeenCalledTimes(6)
})
test('should be defensive against unobserving nullified nodes', () => {
const sizeAfterObserving = observerElementsMap.size + 1
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
const tree = create(component, {
createNodeMock: () => target,
})
tree.getInstance().target = null
tree.getInstance().unobserve()
expect(observerElementsMap.size).toBe(sizeAfterObserving)
})
test('should not reobserve on a second render after root changed the first time', () => {
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
let called = false
const tree = create(component, {
createNodeMock() {
if (called) {
return target
}
called = true
return Object.assign({ id: 2 }, target)
},
})
const instance = tree.getInstance()
const spy1 = jest.spyOn(instance, 'observe')
const spy2 = jest.spyOn(instance, 'unobserve')
tree.update(
<IntersectionObserverComponent onChange={noop}>
<div />
</IntersectionObserverComponent>
)
tree.update(
<IntersectionObserverComponent onChange={noop}>
<div key="forcesRender" />
</IntersectionObserverComponent>
)
expect(spy1).toHaveBeenCalledTimes(1)
expect(spy2).toHaveBeenCalledTimes(1)
})
})
describe('callback', () => {
test('should call propType onChange for each of the changes', () => {
const spy = jest.fn()
const component = (
<IntersectionObserverComponent onChange={spy}>
<span />
</IntersectionObserverComponent>
)
const target1 = Object.assign({ id: 1 }, target)
const target2 = Object.assign({ id: 2 }, target)
const instance = create(component, { createNodeMock: () => target1 }).getInstance()
create(React.cloneElement(component), {
createNodeMock: () => target2,
})
expect(observerElementsMap.size).toBe(1)
const boundingClientRect = {}
const intersectionRect = {}
const entry1 = new IntersectionObserverEntry({
target: target1,
boundingClientRect,
intersectionRect,
})
const entry2 = new IntersectionObserverEntry({
target: target2,
boundingClientRect,
intersectionRect,
})
callback([entry1, entry2], instance.observer)
expect(spy.mock.calls[0][0]).toBe(entry1)
expect(spy.mock.calls[1][0]).toBe(entry2)
})
})
describe('handleChange', () => {
test('should throw with `onlyOnce` if entry lacks `isIntersecting`', () => {
global.spyOn(console, 'error') // suppress deprecation warning
const component = (
<IntersectionObserverComponent onChange={noop} onlyOnce={true}>
<span />
</IntersectionObserverComponent>
)
const instance = create(component, { createNodeMock: () => target }).getInstance()
const boundingClientRect = {}
const intersectionRect = {}
const entry = new IntersectionObserverEntry({
target,
boundingClientRect,
intersectionRect,
})
delete entry.isIntersecting
// skipping @@fork @todo
// expect(() => instance.handleChange(entry)).toThrowErrorMatchingSnapshot()
expect(typeof entry).toEqual('object')
})
test('should unobserve with `onlyOnce` if `isIntersecting` is true', () => {
global.spyOn(console, 'error') // suppress deprecation warning
const component = (
<IntersectionObserverComponent onChange={noop} onlyOnce={true}>
<span />
</IntersectionObserverComponent>
)
const instance = create(component, { createNodeMock: () => target }).getInstance()
const spy = jest.spyOn(instance, 'unobserve')
const boundingClientRect = {}
const intersectionRect = {}
const entry = new IntersectionObserverEntry({
target,
boundingClientRect,
intersectionRect,
})
entry.isIntersecting = true
instance.handleChange(entry)
expect(spy).toBeCalled()
})
test('should not unobserve with `onlyOnce` if `isIntersecting` is false', () => {
global.spyOn(console, 'error') // suppress deprecation warning
const component = (
<IntersectionObserverComponent onChange={noop} onlyOnce={true}>
<span />
</IntersectionObserverComponent>
)
const instance = create(component, { createNodeMock: () => target }).getInstance()
const spy = jest.spyOn(instance, 'unobserve')
const boundingClientRect = {}
const intersectionRect = {}
const entry = new IntersectionObserverEntry({
target,
boundingClientRect,
intersectionRect,
})
entry.isIntersecting = false
instance.handleChange(entry)
expect(spy).not.toBeCalled()
})
test('should warn about the deprecation of `onlyOnce`', () => {
const component = (
<IntersectionObserverComponent onChange={noop} onlyOnce={true}>
<span />
</IntersectionObserverComponent>
)
const spy = global.spyOn(console, 'error')
const instance = create(component, { createNodeMock: () => target }).getInstance()
const boundingClientRect = {}
const intersectionRect = {}
const entry = new IntersectionObserverEntry({
target,
boundingClientRect,
intersectionRect,
})
entry.isIntersecting = true
instance.handleChange(entry)
expect(spy).toBeCalled()
expect(spy.calls.first().args[0]).toContain('deprecation')
})
describe('disabled', () => {
test('should not observe if disabled', () => {
const component = (
<IntersectionObserverComponent onChange={noop} disabled={true}>
<span />
</IntersectionObserverComponent>
)
const sizeBefore = observerElementsMap.size
create(component, { createNodeMock: () => target })
expect(observerElementsMap.size).toBe(sizeBefore)
})
test('should observe if not disabled', () => {
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
const sizeAfterObserving = observerElementsMap.size + 1
create(component, { createNodeMock: () => target })
.getInstance()
expect(observerElementsMap.size).toBe(sizeAfterObserving)
})
test('should observe if no longer disabled', () => {
const component = (
<IntersectionObserverComponent onChange={noop} disabled={true}>
<span />
</IntersectionObserverComponent>
)
const tree = create(component, { createNodeMock: () => target })
const instance = tree.getInstance()
const spy = jest.spyOn(instance, 'observe')
tree.update(
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
expect(spy).toBeCalled()
})
test('should unobserve if disabled', () => {
const component = (
<IntersectionObserverComponent onChange={noop}>
<span />
</IntersectionObserverComponent>
)
const tree = create(component, { createNodeMock: () => target })
const instance = tree.getInstance()
const spy1 = jest.spyOn(instance, 'unobserve')
const spy2 = jest.spyOn(instance, 'observe')
tree.update(
<IntersectionObserverComponent onChange={noop} disabled={true}>
<span />
</IntersectionObserverComponent>
)
expect(spy1).toBeCalled()
expect(spy2).not.toBeCalled()
})
})
})