react-intl 6.0.3 → 6.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/BUILD +130 -0
  2. package/CHANGELOG.md +1384 -0
  3. package/LICENSE.md +0 -0
  4. package/README.md +0 -0
  5. package/example-sandboxes/rescripts/.rescriptsrc.js +28 -0
  6. package/example-sandboxes/rescripts/package-lock.json +18035 -0
  7. package/example-sandboxes/rescripts/package.json +35 -0
  8. package/example-sandboxes/rescripts/public/index.html +42 -0
  9. package/example-sandboxes/rescripts/src/App.tsx +16 -0
  10. package/example-sandboxes/rescripts/src/index.tsx +5 -0
  11. package/example-sandboxes/rescripts/src/react-app-env.d.ts +1 -0
  12. package/example-sandboxes/rescripts/src/styles.css +4 -0
  13. package/example-sandboxes/rescripts/tsconfig.json +20 -0
  14. package/example-sandboxes/strict-locale-type/.env +1 -0
  15. package/example-sandboxes/strict-locale-type/package-lock.json +14595 -0
  16. package/example-sandboxes/strict-locale-type/package.json +35 -0
  17. package/example-sandboxes/strict-locale-type/src/App.tsx +48 -0
  18. package/example-sandboxes/strict-locale-type/src/index.html +28 -0
  19. package/example-sandboxes/strict-locale-type/src/index.tsx +7 -0
  20. package/example-sandboxes/strict-locale-type/src/styles.css +4 -0
  21. package/example-sandboxes/strict-locale-type/tsconfig.json +7 -0
  22. package/example-sandboxes/strict-message-types/.env +1 -0
  23. package/example-sandboxes/strict-message-types/package-lock.json +14596 -0
  24. package/example-sandboxes/strict-message-types/package.json +35 -0
  25. package/example-sandboxes/strict-message-types/src/App.tsx +31 -0
  26. package/example-sandboxes/strict-message-types/src/index.html +28 -0
  27. package/example-sandboxes/strict-message-types/src/index.tsx +7 -0
  28. package/example-sandboxes/strict-message-types/src/styles.css +4 -0
  29. package/example-sandboxes/strict-message-types/tsconfig.json +7 -0
  30. package/examples/BUILD +70 -0
  31. package/examples/Bug2727.tsx +37 -0
  32. package/examples/HandleChange.tsx +48 -0
  33. package/examples/Hooks.tsx +126 -0
  34. package/examples/Injected.tsx +41 -0
  35. package/examples/Messages.tsx +82 -0
  36. package/examples/StaticTypeSafetyAndCodeSplitting/StaticTypeSafetyAndCodeSplitting.tsx +44 -0
  37. package/examples/StaticTypeSafetyAndCodeSplitting/en.json +3 -0
  38. package/examples/StaticTypeSafetyAndCodeSplitting/intlHelpers.tsx +39 -0
  39. package/examples/StaticTypeSafetyAndCodeSplitting/it.json +3 -0
  40. package/examples/TimeZone.tsx +44 -0
  41. package/examples/advanced/Advanced.tsx +68 -0
  42. package/examples/advanced/compiled-lang/en.json +77 -0
  43. package/examples/advanced/compiled-lang/fr.json +77 -0
  44. package/examples/index.html +20 -0
  45. package/examples/index.tsx +44 -0
  46. package/examples/package.json +20 -0
  47. package/index.ts +127 -0
  48. package/jest.config.js +27 -0
  49. package/package.json +12 -8
  50. package/src/components/createFormattedComponent.tsx +123 -0
  51. package/src/components/dateTimeRange.tsx +26 -0
  52. package/src/components/injectIntl.tsx +111 -0
  53. package/src/components/message.tsx +73 -0
  54. package/src/components/plural.tsx +45 -0
  55. package/src/components/provider.tsx +196 -0
  56. package/src/components/relative.tsx +192 -0
  57. package/src/components/useIntl.ts +10 -0
  58. package/src/types.ts +29 -0
  59. package/src/utils.ts +77 -0
  60. package/tests/functional/index.ts +18 -0
  61. package/tests/functional/support/build.ts +16 -0
  62. package/tests/functional/support/format.tsx +112 -0
  63. package/tests/perf/index.tsx +196 -0
  64. package/tests/setup.js +10 -0
  65. package/tests/unit/components/__snapshots__/displayName.tsx.snap +19 -0
  66. package/tests/unit/components/__snapshots__/message.tsx.snap +41 -0
  67. package/tests/unit/components/__snapshots__/relative.tsx.snap +11 -0
  68. package/tests/unit/components/__snapshots__/useIntl.tsx.snap +25 -0
  69. package/tests/unit/components/date.tsx +233 -0
  70. package/tests/unit/components/dateTimeRange.tsx +103 -0
  71. package/tests/unit/components/displayName.tsx +57 -0
  72. package/tests/unit/components/message.tsx +509 -0
  73. package/tests/unit/components/number.tsx +198 -0
  74. package/tests/unit/components/plural.tsx +116 -0
  75. package/tests/unit/components/provider.tsx +215 -0
  76. package/tests/unit/components/relative.tsx +263 -0
  77. package/tests/unit/components/time.tsx +242 -0
  78. package/tests/unit/components/useIntl.tsx +64 -0
  79. package/tests/unit/components/withIntl.tsx +66 -0
  80. package/tests/unit/react-intl.ts +88 -0
  81. package/tests/unit/testUtils.tsx +42 -0
  82. package/tsconfig.json +5 -0
  83. package/index.d.ts +0 -43
  84. package/index.d.ts.map +0 -1
  85. package/index.js +0 -51
  86. package/lib/index.d.ts +0 -43
  87. package/lib/index.d.ts.map +0 -1
  88. package/lib/index.js +0 -26
  89. package/lib/src/components/createFormattedComponent.d.ts +0 -29
  90. package/lib/src/components/createFormattedComponent.d.ts.map +0 -1
  91. package/lib/src/components/createFormattedComponent.js +0 -62
  92. package/lib/src/components/dateTimeRange.d.ts +0 -11
  93. package/lib/src/components/dateTimeRange.d.ts.map +0 -1
  94. package/lib/src/components/dateTimeRange.js +0 -15
  95. package/lib/src/components/injectIntl.d.ts +0 -22
  96. package/lib/src/components/injectIntl.d.ts.map +0 -1
  97. package/lib/src/components/injectIntl.js +0 -29
  98. package/lib/src/components/message.d.ts +0 -12
  99. package/lib/src/components/message.d.ts.map +0 -1
  100. package/lib/src/components/message.js +0 -35
  101. package/lib/src/components/plural.d.ts +0 -15
  102. package/lib/src/components/plural.d.ts.map +0 -1
  103. package/lib/src/components/plural.js +0 -26
  104. package/lib/src/components/provider.d.ts +0 -35
  105. package/lib/src/components/provider.d.ts.map +0 -1
  106. package/lib/src/components/provider.js +0 -108
  107. package/lib/src/components/relative.d.ts +0 -12
  108. package/lib/src/components/relative.d.ts.map +0 -1
  109. package/lib/src/components/relative.js +0 -129
  110. package/lib/src/components/useIntl.d.ts +0 -3
  111. package/lib/src/components/useIntl.d.ts.map +0 -1
  112. package/lib/src/components/useIntl.js +0 -8
  113. package/lib/src/types.d.ts +0 -12
  114. package/lib/src/types.d.ts.map +0 -1
  115. package/lib/src/types.js +0 -1
  116. package/lib/src/utils.d.ts +0 -14
  117. package/lib/src/utils.d.ts.map +0 -1
  118. package/lib/src/utils.js +0 -43
  119. package/react-intl.iife.js +0 -7485
  120. package/src/components/createFormattedComponent.d.ts +0 -29
  121. package/src/components/createFormattedComponent.d.ts.map +0 -1
  122. package/src/components/createFormattedComponent.js +0 -69
  123. package/src/components/dateTimeRange.d.ts +0 -11
  124. package/src/components/dateTimeRange.d.ts.map +0 -1
  125. package/src/components/dateTimeRange.js +0 -17
  126. package/src/components/injectIntl.d.ts +0 -22
  127. package/src/components/injectIntl.d.ts.map +0 -1
  128. package/src/components/injectIntl.js +0 -33
  129. package/src/components/message.d.ts +0 -12
  130. package/src/components/message.d.ts.map +0 -1
  131. package/src/components/message.js +0 -37
  132. package/src/components/plural.d.ts +0 -15
  133. package/src/components/plural.d.ts.map +0 -1
  134. package/src/components/plural.js +0 -29
  135. package/src/components/provider.d.ts +0 -35
  136. package/src/components/provider.d.ts.map +0 -1
  137. package/src/components/provider.js +0 -112
  138. package/src/components/relative.d.ts +0 -12
  139. package/src/components/relative.d.ts.map +0 -1
  140. package/src/components/relative.js +0 -131
  141. package/src/components/useIntl.d.ts +0 -3
  142. package/src/components/useIntl.d.ts.map +0 -1
  143. package/src/components/useIntl.js +0 -12
  144. package/src/types.d.ts +0 -12
  145. package/src/types.d.ts.map +0 -1
  146. package/src/types.js +0 -2
  147. package/src/utils.d.ts +0 -14
  148. package/src/utils.d.ts.map +0 -1
  149. package/src/utils.js +0 -49
@@ -0,0 +1,116 @@
1
+ import * as React from 'react'
2
+ import FormattedPlural from '../../../src/components/plural'
3
+ import {mountFormattedComponentWithProvider} from '../testUtils'
4
+ import {createIntl} from '../../../src/components/provider'
5
+ import {IntlShape} from '@formatjs/intl'
6
+ import {render} from '@testing-library/react'
7
+ import {LDMLPluralRule} from '@formatjs/ecma402-abstract'
8
+
9
+ const mountWithProvider = mountFormattedComponentWithProvider(FormattedPlural)
10
+
11
+ describe('<FormattedPlural>', () => {
12
+ let intl: IntlShape<React.ReactNode>
13
+
14
+ beforeEach(() => {
15
+ intl = createIntl({
16
+ onError: () => {},
17
+ locale: 'en',
18
+ })
19
+ })
20
+
21
+ it('has a `displayName`', () => {
22
+ expect(typeof FormattedPlural.displayName).toBe('string')
23
+ })
24
+
25
+ it('throws when <IntlProvider> is missing from ancestry', () => {
26
+ // So it doesn't spam the console
27
+ jest.spyOn(console, 'error').mockImplementation(() => {})
28
+ expect(() => render(<FormattedPlural value={1} other="" />)).toThrow(
29
+ '[React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.'
30
+ )
31
+ })
32
+
33
+ it('renders an empty <> when no `other` prop is provided', () => {
34
+ let {getByTestId, unmount} = mountWithProvider(
35
+ // @ts-ignore
36
+ {value: undefined, other: undefined},
37
+ intl
38
+ )
39
+
40
+ expect(getByTestId('comp')).toHaveTextContent('')
41
+ unmount()
42
+ getByTestId = mountWithProvider(
43
+ {value: 1, other: undefined},
44
+ intl
45
+ ).getByTestId
46
+ expect(getByTestId('comp')).toHaveTextContent('')
47
+ })
48
+
49
+ it('renders `other` in a <> when no `value` prop is provided', () => {
50
+ const other = 'Jest'
51
+
52
+ const {getByTestId} = mountWithProvider(
53
+ {
54
+ // @ts-ignore
55
+ value: undefined,
56
+ other,
57
+ },
58
+ intl
59
+ )
60
+ expect(getByTestId('comp')).toHaveTextContent(other)
61
+ })
62
+
63
+ it('renders a formatted plural in a <>', () => {
64
+ const num = 1
65
+ const one = 'foo'
66
+ const other = 'bar'
67
+
68
+ const {getByTestId} = mountWithProvider({value: num, one, other}, intl)
69
+ expect(getByTestId('comp')).toHaveTextContent(num === 1 ? one : other)
70
+ })
71
+
72
+ it('renders a formatted plural w/o textComponent', () => {
73
+ const num = 1
74
+ const one = 'foo'
75
+ const other = 'bar'
76
+
77
+ const {getByTestId} = mountWithProvider(
78
+ {value: num, one, other},
79
+ // @ts-ignore
80
+ {...intl, textComponent: null}
81
+ )
82
+ expect(getByTestId('comp')).toHaveTextContent(num === 1 ? one : other)
83
+ })
84
+
85
+ it('accepts valid IntlPluralFormat options as props', () => {
86
+ const num = 22
87
+ const props = {two: 'nd'} as any
88
+ const options = {type: 'ordinal' as 'ordinal'}
89
+
90
+ const {getByTestId} = mountWithProvider(
91
+ {value: num, ...props, ...options},
92
+ intl
93
+ )
94
+
95
+ expect(getByTestId('comp')).toHaveTextContent(
96
+ props[intl.formatPlural(num, options)]
97
+ )
98
+ })
99
+
100
+ it('supports function-as-child pattern', () => {
101
+ const props = {one: 'foo'} as Record<LDMLPluralRule, string>
102
+ const num = 1
103
+
104
+ const spy = jest.fn().mockImplementation(() => <b data-testid="b">Jest</b>)
105
+ const {getByTestId} = mountWithProvider(
106
+ {...props, other: undefined, value: num, children: spy},
107
+ intl
108
+ )
109
+
110
+ expect(spy).toHaveBeenCalled()
111
+ expect(spy.mock.calls[0]).toEqual([props[intl.formatPlural(num)]])
112
+
113
+ expect(getByTestId('b').tagName).toBe('B')
114
+ expect(getByTestId('comp')).toHaveTextContent('Jest')
115
+ })
116
+ })
@@ -0,0 +1,215 @@
1
+ import * as React from 'react'
2
+ import IntlProvider from '../../../src/components/provider'
3
+ import withIntl from '../../../src/components/injectIntl'
4
+ import {render} from '@testing-library/react'
5
+ import {FormattedDate, FormattedMessage} from '../../../'
6
+ import type {IntlConfig} from '../../../src/types'
7
+
8
+ describe('<IntlProvider>', () => {
9
+ const now = Date.now()
10
+
11
+ class Child extends React.Component<any> {
12
+ render() {
13
+ return <span data-testid="foo">{'foo'}</span>
14
+ }
15
+ }
16
+
17
+ const IntlChild = withIntl(Child)
18
+
19
+ let dateNow: jest.SpyInstance
20
+
21
+ beforeEach(() => {
22
+ dateNow = jest.spyOn(Date, 'now').mockImplementation(() => now)
23
+ })
24
+
25
+ afterEach(() => {
26
+ dateNow.mockRestore()
27
+ })
28
+
29
+ it('has a `displayName`', () => {
30
+ expect(typeof IntlProvider.displayName).toBe('string')
31
+ })
32
+
33
+ it('warns when no `locale` prop is provided', () => {
34
+ const onError = jest.fn()
35
+ render(
36
+ <IntlProvider
37
+ // @ts-ignore
38
+ locale={undefined}
39
+ onError={onError}
40
+ >
41
+ <IntlChild />
42
+ </IntlProvider>
43
+ )
44
+
45
+ expect(onError.mock.calls[0][0].code).toBe('INVALID_CONFIG')
46
+ expect(onError).toHaveBeenCalledTimes(1)
47
+ })
48
+
49
+ it('should re-render with new messages', () => {
50
+ const onError = jest.fn()
51
+ const props: IntlConfig = {
52
+ locale: 'en',
53
+ timeZone: 'Australia/Adelaide',
54
+ formats: {
55
+ date: {
56
+ 'year-only': {
57
+ year: 'numeric',
58
+ },
59
+ },
60
+ },
61
+ messages: {
62
+ hello: 'Hello, World!',
63
+ },
64
+
65
+ defaultLocale: 'fr',
66
+ defaultFormats: {
67
+ date: {
68
+ 'year-only': {
69
+ year: 'numeric',
70
+ },
71
+ },
72
+ },
73
+ onError,
74
+ }
75
+
76
+ const {getByTestId, rerender} = render(
77
+ <IntlProvider {...props}>
78
+ <span data-testid="comp">
79
+ <FormattedMessage id="hello" />
80
+ </span>
81
+ </IntlProvider>
82
+ )
83
+
84
+ expect(onError).not.toHaveBeenCalled()
85
+ expect(getByTestId('comp')).toHaveTextContent('Hello, World!')
86
+ rerender(
87
+ <IntlProvider {...props} messages={{hello: 'blah'}}>
88
+ <span data-testid="comp">
89
+ <FormattedMessage id="hello" />
90
+ </span>
91
+ </IntlProvider>
92
+ )
93
+ expect(getByTestId('comp')).toHaveTextContent('blah')
94
+ })
95
+
96
+ it('warns when `locale` prop provided has no locale data in Intl.NumberFormat', () => {
97
+ const locale = 'missing'
98
+ const onError = jest.fn()
99
+ render(
100
+ <IntlProvider locale={locale} onError={onError}>
101
+ <IntlChild />
102
+ </IntlProvider>
103
+ )
104
+
105
+ expect(onError.mock.calls[0][0].code).toBe('MISSING_DATA')
106
+ expect(onError).toHaveBeenCalledTimes(1)
107
+ })
108
+
109
+ it('warns when `locale` prop provided has no locale data in Intl.DateTimeFormat', () => {
110
+ const locale = 'xx-HA'
111
+ const onError = jest.fn()
112
+ const supportedLocalesOf = Intl.NumberFormat.supportedLocalesOf
113
+ Intl.NumberFormat.supportedLocalesOf = (): string[] => ['xx-HA']
114
+ render(
115
+ <IntlProvider locale={locale} onError={onError}>
116
+ <IntlChild />
117
+ </IntlProvider>
118
+ )
119
+
120
+ expect(onError.mock.calls[0][0].code).toBe('MISSING_DATA')
121
+ expect(onError).toHaveBeenCalledTimes(1)
122
+ Intl.NumberFormat.supportedLocalesOf = supportedLocalesOf
123
+ })
124
+
125
+ it('renders its `children`', () => {
126
+ const el = (
127
+ <IntlProvider locale="en">
128
+ <IntlChild />
129
+ </IntlProvider>
130
+ )
131
+
132
+ const {container} = render(el)
133
+ expect(container).toHaveTextContent('foo')
134
+ })
135
+
136
+ it('shadows inherited intl config props from an <IntlProvider> ancestor', () => {
137
+ const onError = jest.fn()
138
+ const props: IntlConfig = {
139
+ locale: 'en',
140
+ timeZone: 'Australia/Adelaide',
141
+ formats: {
142
+ date: {
143
+ 'year-only': {
144
+ year: 'numeric',
145
+ },
146
+ },
147
+ },
148
+ messages: {
149
+ hello: 'Hello, World!',
150
+ },
151
+
152
+ defaultLocale: 'fr',
153
+ defaultFormats: {
154
+ date: {
155
+ 'year-only': {
156
+ year: 'numeric',
157
+ },
158
+ },
159
+ },
160
+ onError,
161
+ }
162
+
163
+ const {getByTestId} = render(
164
+ <IntlProvider {...props}>
165
+ <IntlProvider
166
+ locale="fr"
167
+ timeZone="Atlantic/Azores"
168
+ formats={{}}
169
+ messages={{}}
170
+ defaultLocale="en"
171
+ defaultFormats={{}}
172
+ textComponent="span"
173
+ >
174
+ <span data-testid="comp">
175
+ <FormattedDate value={new Date(2020, 1, 1)} timeZoneName="short" />
176
+ </span>
177
+ </IntlProvider>
178
+ </IntlProvider>
179
+ )
180
+
181
+ expect(onError).not.toHaveBeenCalled()
182
+ expect(getByTestId('comp')).toHaveTextContent('31/01/2020')
183
+ })
184
+ it('show warning for non-AST messages with defaultRichTextElements', () => {
185
+ const consoleWarn = jest
186
+ .spyOn(console, 'warn')
187
+ .mockImplementation(() => null)
188
+
189
+ render(
190
+ <IntlProvider
191
+ locale="fr"
192
+ timeZone="Atlantic/Azores"
193
+ formats={{}}
194
+ messages={{
195
+ foo: 'bar',
196
+ }}
197
+ defaultLocale="en"
198
+ defaultFormats={{}}
199
+ textComponent="span"
200
+ defaultRichTextElements={{
201
+ b: chunks => <b>{chunks}</b>,
202
+ }}
203
+ >
204
+ <span data-testid="comp">
205
+ <FormattedDate value={new Date(2020, 1, 1)} timeZoneName="short" />
206
+ </span>
207
+ </IntlProvider>
208
+ )
209
+
210
+ expect(consoleWarn)
211
+ .toHaveBeenCalledWith(`[@formatjs/intl] "defaultRichTextElements" was specified but "message" was not pre-compiled.
212
+ Please consider using "@formatjs/cli" to pre-compile your messages for performance.
213
+ For more details see https://formatjs.io/docs/getting-started/message-distribution`)
214
+ })
215
+ })
@@ -0,0 +1,263 @@
1
+ import '@formatjs/intl-relativetimeformat/polyfill'
2
+ import * as React from 'react'
3
+ import FormattedRelativeTime from '../../../src/components/relative'
4
+ import {createIntl} from '../../../src/components/provider'
5
+ import {IntlShape} from '../../../src/types'
6
+ import {mountFormattedComponentWithProvider} from '../testUtils'
7
+ import type {IntlConfig} from '../../../src/types'
8
+ import {render, act} from '@testing-library/react'
9
+
10
+ jest.useFakeTimers()
11
+
12
+ const mountWithProvider = mountFormattedComponentWithProvider(
13
+ FormattedRelativeTime
14
+ )
15
+
16
+ describe('<FormattedRelativeTime>', () => {
17
+ let intl: IntlShape
18
+ const intlConfig: IntlConfig = {
19
+ locale: 'en',
20
+ onError: () => {},
21
+ }
22
+
23
+ beforeEach(() => {
24
+ intl = createIntl(intlConfig)
25
+ })
26
+
27
+ it('has a `displayName`', () => {
28
+ expect(FormattedRelativeTime.displayName).toBe('FormattedRelativeTime')
29
+ })
30
+
31
+ it('throws when <IntlProvider> is missing from ancestry', () => {
32
+ // So it doesn't spam the console
33
+ jest.spyOn(console, 'error').mockImplementation(() => {})
34
+ expect(() => render(<FormattedRelativeTime />)).toThrow(
35
+ '[React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.'
36
+ )
37
+ })
38
+
39
+ it('should re-render when props change', () => {
40
+ const spy = jest.fn().mockImplementation(() => null)
41
+ mountWithProvider({value: 0, children: spy}, intlConfig)
42
+ mountWithProvider({value: 1, children: spy}, intlConfig)
43
+ expect(spy).toHaveBeenCalledTimes(2)
44
+ })
45
+
46
+ it('should re-render when context changes', () => {
47
+ const otherIntl = createIntl({
48
+ locale: 'en-US',
49
+ })
50
+ const spy = jest.fn().mockImplementation(() => null)
51
+ mountWithProvider({value: 0, children: spy}, intlConfig)
52
+ mountWithProvider({value: 0, children: spy}, otherIntl)
53
+
54
+ expect(spy).toHaveBeenCalledTimes(2)
55
+ })
56
+
57
+ it('accepts valid IntlRelativeTimeFormat options as props', () => {
58
+ const options = {style: 'narrow' as 'narrow'}
59
+ const {getByTestId} = mountWithProvider({value: -60, ...options})
60
+
61
+ expect(getByTestId('comp')).toHaveTextContent(
62
+ intl.formatRelativeTime(-60, 'second', options)
63
+ )
64
+ })
65
+
66
+ it('can render in null textComponent', () => {
67
+ const options = {style: 'narrow' as 'narrow'}
68
+ const {getByTestId} = mountWithProvider(
69
+ {value: -60, ...options},
70
+ {
71
+ ...intlConfig,
72
+ // @ts-ignore
73
+ textComponent: null,
74
+ }
75
+ )
76
+
77
+ expect(getByTestId('comp')).toHaveTextContent(
78
+ intl.formatRelativeTime(-60, 'second', options)
79
+ )
80
+ })
81
+
82
+ it('throws an error for invalid unit', () => {
83
+ const onError = jest.fn()
84
+ const {getByTestId} = mountWithProvider(
85
+ {value: 0, unit: 'invalid' as any},
86
+ {onError, locale: 'en'}
87
+ )
88
+ expect(getByTestId('comp')).toHaveTextContent('0')
89
+ expect(onError).toHaveBeenCalledTimes(1)
90
+ expect(onError.mock.calls[0][0].code).toBe('FORMAT_ERROR')
91
+ })
92
+
93
+ it('accepts `format` prop', () => {
94
+ const format = 'seconds'
95
+ const config: IntlConfig = {
96
+ onError: () => {},
97
+ locale: 'en',
98
+ formats: {
99
+ relative: {
100
+ [format]: {
101
+ style: 'narrow',
102
+ },
103
+ },
104
+ },
105
+ }
106
+ intl = createIntl(config)
107
+
108
+ const {getByTestId} = mountWithProvider({value: -60, format}, config)
109
+
110
+ expect(getByTestId('comp')).toHaveTextContent(
111
+ intl.formatRelativeTime(-60, 'second', {format})
112
+ )
113
+ })
114
+
115
+ it('supports function-as-child pattern', () => {
116
+ const spy = jest.fn().mockImplementation(() => <b>Jest</b>)
117
+ const {getByTestId} = mountWithProvider(
118
+ {value: 0, children: spy},
119
+ intlConfig
120
+ )
121
+
122
+ expect(spy).toHaveBeenCalledTimes(1)
123
+ expect(spy.mock.calls[0]).toEqual([intl.formatRelativeTime(0)])
124
+
125
+ expect(getByTestId('comp')).toMatchSnapshot()
126
+ })
127
+
128
+ xit('updates automatically', () => {
129
+ // span bc enzyme support for </> seems buggy
130
+ const {getByTestId} = mountWithProvider(
131
+ {value: 2, updateIntervalInSeconds: 1},
132
+ {...intl}
133
+ )
134
+ expect(getByTestId('comp')).toHaveTextContent(
135
+ intl.formatRelativeTime(2, 'second')
136
+ )
137
+ act(() => {
138
+ jest.advanceTimersByTime(1010)
139
+ })
140
+ expect(getByTestId('comp')).toHaveTextContent(
141
+ intl.formatRelativeTime(1, 'second')
142
+ )
143
+ act(() => {
144
+ jest.advanceTimersByTime(1010)
145
+ })
146
+ expect(getByTestId('comp')).toHaveTextContent(
147
+ intl.formatRelativeTime(0, 'second')
148
+ )
149
+ act(() => {
150
+ jest.advanceTimersByTime(1010)
151
+ })
152
+ expect(getByTestId('comp')).toHaveTextContent(
153
+ intl.formatRelativeTime(-1, 'second')
154
+ )
155
+ })
156
+
157
+ xit('updates when the `value` prop changes', () => {
158
+ const {getByTestId, rerenderProps} = mountWithProvider(
159
+ {value: 0, updateIntervalInSeconds: 1},
160
+ {...intl}
161
+ )
162
+ rerenderProps({value: 10, updateIntervalInSeconds: 1}, {...intl})
163
+
164
+ expect(getByTestId('comp')).toHaveTextContent('in 10 seconds')
165
+ act(() => {
166
+ jest.advanceTimersByTime(1010)
167
+ })
168
+
169
+ expect(getByTestId('comp')).toHaveTextContent('in 9 seconds')
170
+ })
171
+
172
+ xit('should adjust unit to min correctly', function () {
173
+ // span bc enzyme support for </> seems buggy
174
+ const {getByTestId} = mountWithProvider(
175
+ {value: -59, updateIntervalInSeconds: 1},
176
+ {...intlConfig}
177
+ )
178
+ act(() => {
179
+ jest.advanceTimersByTime(1010)
180
+ })
181
+ expect(getByTestId('comp')).toHaveTextContent(
182
+ intl.formatRelativeTime(-1, 'minute')
183
+ )
184
+ })
185
+ xit('should adjust unit to min correctly even if updateIntervalInSeconds goes past that ts', function () {
186
+ // span bc enzyme support for </> seems buggy
187
+ const {getByTestId} = mountWithProvider(
188
+ {value: -59, updateIntervalInSeconds: 2},
189
+ {...intlConfig}
190
+ )
191
+ act(() => {
192
+ jest.advanceTimersByTime(1010)
193
+ })
194
+ expect(getByTestId('comp')).toHaveTextContent(
195
+ intl.formatRelativeTime(-1, 'minute')
196
+ )
197
+ })
198
+ xit('should adjust unit to hour correctly', function () {
199
+ // span bc enzyme support for </> seems buggy
200
+ const {getByTestId} = mountWithProvider(
201
+ {value: -59, unit: 'minute', updateIntervalInSeconds: 1},
202
+ {...intlConfig}
203
+ )
204
+ // Advance 1 min
205
+ act(() => {
206
+ jest.advanceTimersByTime(1000 * 60)
207
+ })
208
+ expect(getByTestId('comp')).toHaveTextContent(
209
+ intl.formatRelativeTime(-1, 'hour')
210
+ )
211
+ })
212
+ xit('should adjust unit to day correctly and stop', function () {
213
+ // span bc enzyme support for </> seems buggy
214
+ const {getByTestId} = mountWithProvider(
215
+ {value: -23, unit: 'hour', updateIntervalInSeconds: 1},
216
+ {...intlConfig}
217
+ )
218
+ // Advance 1 hour
219
+ act(() => {
220
+ jest.advanceTimersByTime(1000 * 60 * 60)
221
+ })
222
+ expect(getByTestId('comp')).toHaveTextContent(
223
+ intl.formatRelativeTime(-1, 'day')
224
+ )
225
+ // Advance 1 day
226
+ act(() => {
227
+ jest.advanceTimersByTime(1000 * 60 * 60 * 24)
228
+ })
229
+ // shouldn't change anything
230
+ expect(getByTestId('comp')).toHaveTextContent(
231
+ intl.formatRelativeTime(-1, 'day')
232
+ )
233
+ })
234
+ xit('should show high seconds values as days with no timer', function () {
235
+ // span bc enzyme support for </> seems buggy
236
+ const {getByTestId} = mountWithProvider(
237
+ {value: -(60 * 60 * 24 * 3), unit: 'second', updateIntervalInSeconds: 1},
238
+ {...intlConfig}
239
+ )
240
+ expect(getByTestId('comp')).toHaveTextContent(
241
+ intl.formatRelativeTime(-3, 'day')
242
+ )
243
+ })
244
+ xit('should throw if try to increment in day', function () {
245
+ // span bc enzyme support for </> seems buggy
246
+ expect(() =>
247
+ mountWithProvider(
248
+ {value: 5, unit: 'day', updateIntervalInSeconds: 1},
249
+ {...intlConfig}
250
+ )
251
+ ).toThrow('Cannot schedule update with unit longer than hour')
252
+ })
253
+ it('should clear timer on unmount', function () {
254
+ const {unmount, rerenderProps} = mountWithProvider(
255
+ {value: 0, updateIntervalInSeconds: 1},
256
+ {...intlConfig}
257
+ )
258
+ rerenderProps()
259
+ const clearTimeoutSpy = jest.spyOn(window, 'clearTimeout')
260
+ unmount()
261
+ expect(clearTimeoutSpy).toHaveBeenCalled()
262
+ })
263
+ })