Skip to content

Commit 2787e07

Browse files
committed
test(ui-truncate-text): migrate old TruncateText tests
1 parent 66b1ad9 commit 2787e07

File tree

12 files changed

+798
-689
lines changed

12 files changed

+798
-689
lines changed

cypress/component/Truncate.cy.tsx

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2015 - present Instructure, Inc.
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
import React from 'react'
25+
import { expect } from 'chai'
26+
27+
import '../support/component'
28+
import { within } from '@instructure/ui-utils'
29+
import truncate from '../../packages/ui-truncate-text/src/TruncateText/utils/truncate'
30+
31+
32+
describe('truncate', () => {
33+
const defaultText = 'Hello world! This is a long string that should truncate'
34+
const baseStyle = {
35+
fontSize: '16px',
36+
fontFamily: 'Arial',
37+
fontWeight: 'normal',
38+
fontStyle: 'normal',
39+
letterSpacing: 'normal'
40+
}
41+
42+
it('should truncate text when no options are given', async () => {
43+
cy.mount(
44+
<div id="stage" style={{ ...baseStyle, width: '200px' }}>
45+
{defaultText}
46+
</div>
47+
)
48+
49+
cy.get('#stage').then(($stage) => {
50+
truncate($stage[0])
51+
const text = $stage.text()
52+
53+
expect(text.indexOf('truncate')).to.equal(-1)
54+
expect(text.indexOf('\u2026')).to.not.equal(-1)
55+
})
56+
57+
cy.get('#stage').should('have.text', 'Hello world! This is a long…')
58+
})
59+
60+
it('should truncate in the middle of a string', async () => {
61+
cy.mount(
62+
<div id="stage" style={{ ...baseStyle, width: '200px' }}>
63+
{defaultText}
64+
</div>
65+
)
66+
67+
cy.get('#stage').then(($stage) => {
68+
truncate($stage[0], { position: 'middle' })
69+
const text = $stage.text()
70+
71+
expect(text.indexOf('long')).to.equal(-1)
72+
expect(text.indexOf('Hello')).to.not.equal(-1)
73+
expect(text.indexOf('truncate')).to.not.equal(-1)
74+
expect(text.indexOf('\u2026')).to.not.equal(-1)
75+
})
76+
77+
cy.get('#stage').should('have.text', 'Hello world! …ould truncate')
78+
})
79+
80+
it('should truncate at words', async () => {
81+
cy.mount(
82+
<div id="stage" style={{ ...baseStyle, width: '220px' }}>
83+
{defaultText}
84+
</div>
85+
)
86+
87+
cy.get('#stage').then(($stage) => {
88+
truncate($stage[0], { truncate: 'word' })
89+
const text = $stage.text()
90+
91+
expect(text.indexOf('string')).to.equal(-1)
92+
expect(text.indexOf('st')).to.equal(-1)
93+
expect(text.indexOf('long')).to.not.equal(-1)
94+
})
95+
96+
cy.get('#stage').should('have.text', 'Hello world! This is a long …')
97+
})
98+
99+
it('should allow custom ellipsis', async () => {
100+
cy.mount(
101+
<div id="stage" style={{ ...baseStyle, width: '200px' }}>
102+
{defaultText}
103+
</div>
104+
)
105+
106+
cy.get('#stage').then(($stage) => {
107+
truncate($stage[0], { ellipsis: '(...)' })
108+
const text = $stage.text()
109+
110+
expect(text!.slice(-5)).to.equal('(...)')
111+
})
112+
113+
cy.get('#stage').should('have.text', 'Hello world! This is a lon(...)')
114+
})
115+
116+
it('should preserve node structure', async () => {
117+
cy.mount(
118+
<div style={{ ...baseStyle, width: '200px' }}>
119+
<p id="stage" className="testClass">
120+
Hello world! <strong>This is a</strong> long string that{' '}
121+
<em>should truncate</em>
122+
</p>
123+
</div>
124+
)
125+
126+
cy.get('#stage').then(($stage) => {
127+
truncate($stage[0])
128+
129+
cy.wrap($stage[0].childNodes[1].nodeType).should('equal', 1)
130+
cy.wrap($stage[0].childNodes[2].nodeType).should('equal', 3)
131+
cy.wrap($stage[0].children.length).should('equal', 2)
132+
cy.wrap($stage[0].className).should('equal', 'testClass')
133+
cy.wrap($stage[0].tagName).should('equal', 'P')
134+
})
135+
136+
cy.get('strong').should('exist')
137+
cy.get('#stage').should('have.text', 'Hello world! This is a lon…')
138+
})
139+
140+
it('should preserve attributes on nodes', async () => {
141+
cy.mount(
142+
<div style={{ ...baseStyle, width: '200px' }}>
143+
<span id="stage">
144+
This is a{' '}
145+
<a id="link" href="http://google.com" className="tester">
146+
text link
147+
</a>{' '}
148+
with classes and an href.
149+
</span>
150+
</div>
151+
)
152+
153+
cy.get('#stage').then(($stage) => {
154+
truncate($stage[0])
155+
})
156+
157+
cy.get('#link')
158+
.should('have.attr', 'href', 'http://google.com')
159+
.and('have.attr', 'class', 'tester')
160+
.and('have.attr', 'id', 'link')
161+
162+
cy.get('#link').then(($link) => {
163+
const attributesLength = $link[0].attributes.length
164+
expect(attributesLength).to.equal(3)
165+
})
166+
})
167+
168+
it('should calculate max width properly', async () => {
169+
cy.mount(
170+
<div style={{ ...baseStyle, width: 'auto' }}>
171+
<div>
172+
<span id="textContainer">{defaultText}</span>
173+
<div style={{ ...baseStyle, width: '100px' }}>
174+
<div id="stage">{defaultText}</div>
175+
</div>
176+
</div>
177+
</div>
178+
)
179+
180+
cy.get('#stage').then(($stage) => {
181+
const result = truncate($stage[0])
182+
const maxWidth = result!.constraints.width
183+
184+
cy.get('#textContainer').then(($textContainer) => {
185+
const actualMax = $textContainer[0].getBoundingClientRect().width
186+
187+
expect(within(maxWidth, actualMax, 1)).to.equal(true)
188+
})
189+
})
190+
})
191+
192+
it('should calculate `maxLines: auto` correctly', async () => {
193+
cy.mount(
194+
<div
195+
style={{
196+
...baseStyle,
197+
width: '50px',
198+
height: '180px',
199+
lineHeight: 2.8
200+
}}
201+
>
202+
<span id="stage">{defaultText}</span>
203+
</div>
204+
)
205+
206+
cy.get('#stage').then(($stage) => {
207+
const result = truncate($stage[0], { maxLines: 'auto' })
208+
const text = $stage.text()
209+
210+
cy.wrap(text).should('not.equal', defaultText)
211+
cy.wrap(text.length).should('not.equal', 1)
212+
cy.wrap(result!.constraints.lines).should('equal', 4)
213+
})
214+
})
215+
216+
it('should calculate height correctly when `maxLines` is not `auto`', async () => {
217+
cy.mount(
218+
<div
219+
style={{
220+
...baseStyle,
221+
width: '200px',
222+
height: '200px',
223+
lineHeight: 1.4
224+
}}
225+
>
226+
<span id="stage">{defaultText}</span>
227+
</div>
228+
)
229+
230+
cy.get('#stage').then(($stage) => {
231+
const result = truncate($stage[0])
232+
const text = $stage.text()
233+
234+
cy.wrap(text.length).should('not.equal', 1)
235+
236+
cy.wrap(text).should('not.equal', defaultText)
237+
cy.wrap(result!.constraints.height).should('equal', 22.4)
238+
})
239+
})
240+
241+
it('should escape node content', async () => {
242+
cy.spy(console, 'log').as('consoleLogSpy')
243+
const content = '"><img src=a onerror=console.log("hello world") />'
244+
245+
cy.mount(
246+
<div style={{ ...baseStyle, width: '1000px', height: '200px' }}>
247+
<span id="stage">{content}</span>
248+
</div>
249+
)
250+
251+
cy.get('#stage').then(($stage) => {
252+
truncate($stage[0])
253+
254+
cy.wrap($stage.text()).should('equal', content)
255+
cy.get('@consoleLogSpy').should('not.have.been.calledWith', 'hello world')
256+
})
257+
})
258+
259+
it('should truncate when visually hidden', async () => {
260+
cy.mount(
261+
<div
262+
id="stage-wrapper"
263+
style={{ ...baseStyle, width: '200px', opacity: 0 }}
264+
>
265+
<span id="stage">{defaultText}</span>
266+
</div>
267+
)
268+
269+
cy.get('#stage').then(($stage) => {
270+
truncate($stage[0])
271+
const text = $stage.text()
272+
273+
expect(text.indexOf('truncate')).to.equal(-1)
274+
expect(text.indexOf('\u2026')).to.not.equal(-1)
275+
})
276+
277+
cy.get('#stage-wrapper').should('have.css', 'opacity', '0')
278+
cy.get('#stage').should('have.text', 'Hello world! This is a long…')
279+
})
280+
281+
it('should account for font size styles', async () => {
282+
cy.mount(
283+
<div id="stage" style={{ ...baseStyle, width: '200px', fontSize: '16px' }}>
284+
{defaultText}
285+
</div>
286+
)
287+
288+
cy.get('#stage').then(($stageInitial) => {
289+
truncate($stageInitial[0])
290+
})
291+
292+
cy.get('#stage').should('have.text', 'Hello world! This is a long…')
293+
294+
// Update font size
295+
cy.get('#stage').invoke('css', { ...baseStyle, width: '200px', fontSize: '24px' })
296+
297+
cy.get('#stage').then(($stageUpdated) => {
298+
truncate($stageUpdated[0])
299+
})
300+
301+
cy.get('#stage').should('have.text', 'Hello world! This…')
302+
})
303+
})

0 commit comments

Comments
 (0)