๐Ÿฆ‘ testing-library/react๋กœ๋Š” ๋ง์ค„์ž„ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์—†๋‹ค.

testing-library/React๋กœ๋Š” ๋ง์ค„์ž„(truncate) ์ฒดํฌ๋ฅผ ํ•  ์ˆ˜ ์—†์—ˆ๋˜ ์ด์œ  โ€” Cypress๋ฅผ ์“ฐ์ž

FlyingSquirrel
8 min readDec 5, 2020

testing-library/react์—์„œ๋Š” scrollWidth์™€ clientWidth๋Š” ์–ธ์ œ๋‚˜ 0 ๐Ÿ˜‡

์–ด๋ ธ์„ ๋•Œ ๋™์ƒ์ด๋ž‘ ์ข…์ข… ๋ดค๋˜ ์ •๊ธ€์€ ์–ธ์ œ๋‚˜ ๋ง‘์Œ ๋’ค ํ๋ฆผ์ด๋ผ๋Š” ๋งŒํ™” (์ถœ์ฒ˜: ๋‚˜๋ฌด์œ„ํ‚ค)

๊ทผ๋ž˜์— ๋ฆฌ์•กํŠธ๋กœ ํšŒ์‚ฌ์—์„œ ๊ณต์šฉ์œผ๋กœ ์“ฐ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๋ฉด์„œ ๋ง์ค„์ž„(โ€ฆ)์ด ์ž˜ ๋˜๋Š”์ง€ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์“ฐ๊ณ  ์‹ถ์€๋ฐ, ๋งˆ์Œ์ฒ˜๋Ÿผ ์ž˜ ์•ˆ๋์–ด์š”.

function isTextTruncated(element) {
return element.scrollWidth > element.clientWidth;
}

์œ„์™€ ๊ฐ™์ด scrollWidth๊ฐ€ clientWidth๋ณด๋‹ค ๊ธธ์–ด์กŒ๋‹ค๋ฉด ๋ง์ค„์ž„ ์ฒ˜๋ฆฌ๊ฐ€ ๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฑธ๋กœ ํ…Œ์ŠคํŠธํ•˜๋ฉด ๋˜๊ฒ ๋‹ค! ์‹ถ์—ˆ์–ด์š”. ๊ทธ๋Ÿฐ๋ฐ scrollWidth์™€ clientWidth๊ฐ€ 0์„ returnํ•ด์„œ ๊ฐ’์„ ๋น„๊ตํ•  ์ˆ˜๊ฐ€ ์—†๋”๋ผ๊ณ ์š”. ํ—..๐Ÿคญ

์ฝ”๋“œ๋Š” ์ž˜๋ชป์ด ์—†์ง€, ๋‚ด๊ฐ€ ์ž˜๋ชปํ•œ ๊ฑฐ์ง€๋ผ๋Š” ์ƒ๊ฐ์œผ๋กœ ๊ตฌ๊ธ€๋ง์„ ํ•˜๋‹ค๊ฐ€ testing-library/react-testing-library Github ์ด์Šˆ ํŽ˜์ด์ง€์—์„œ ์ €๋ž‘ ๋น„์Šทํ•œ ์งˆ๋ฌธ์„ ํ•œ ์‚ฌ๋žŒ์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฑฐ๊ธฐ์— ๊ต‰์žฅํ•˜๊ณ  ๊ต‰์žฅํ•œ ๋ถ„์ธ ์ผ„ํŠธ์”จ๊ฐ€ ์นœ์ ˆํ•˜๊ฒŒ ๋‹ต๋ณ€์„ ํ•ด์ฃผ์…จ๋Š”๋ฐโ€ฆ

์‘ ์•ˆ๋ผ (์Œ. 3๋ฒˆ Use Cypress์— ๋ˆˆ๊ธธ์ด ๊ฐ”์–ด์š” ์™ ์ง€)

testing-library๋Š” ๊ธฐ๋ณธ์ ์„ JSDOM์„ ์ด์šฉํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด JSDOM์€ layout์— ๋Œ€ํ•œ ์ง€์›์„ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, scrollWidth, clientWidth๊ฐ™์ด ์ธก์ •๋˜๋Š” ๊ฐ’์€ ํ•ญ์ƒ 0์„ ๋ฆฌํ„ดํ•œ๋‹ค๊ณ  ํ•˜๋„ค์š”!

JSDOM GithubํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ด์„œ ์‚ฌ์‹ค์ธ์ง€ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค.

JSDOM์€ Layout์„ ์ง€์›ํ•˜์ง€ ์•Š์•„์š”.

Note that jsdom still does not do any layout or rendering, so this is really just about pretending to be visual, not about implementing the parts of the platform a real, visual web browser would implement.

(์˜์—ญ) jsdom์€ ์—ฌ์ „ํžˆ ๋ ˆ์ด์•„์›ƒ์ด๋‚˜ ๋ Œ๋”๋ง ์ž‘์—…์„ ํ•˜์ง€ ์•Š๋‹ค๋Š” ์ ์„ ์ฃผ์˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ์ ์œผ๋กœ (๋ Œ๋”๋ง ๋“ฑ์˜ ์ž‘์—…์„)ํ•˜๋Š” ์ฒ™ํ•˜๊ณ  ์žˆ์ง€๋งŒ, ์ง„์งœ ๋ธŒ๋ผ์šฐ์ €์˜ ์ผ๋ถ€์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ReadmeํŒŒ์ผ์„ ์ฝ๋‹ค๋ณด๋‹ˆ ์ด๋Ÿฐ ๋ง์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ„ํŠธ์”จ์˜ ๋ง์ฒ˜๋Ÿผ jsdom์€ ์ง€์›์„ ํ•˜์ง€ ์•Š๋Š”๋‹ค๋„ค์š”. does not do any layout or rendering ๋ถ€๋ถ„์— ๋‹ค๋ฅธ ๋ฌธ์„œ ๋งํฌ๊ฐ€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๊ธธ๋ž˜ ํƒ€๊ณ  ๋“ค์–ด๊ฐ€๋ณด์•˜์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์„œ์—์„œ๋Š” ์šฐ๋ฆฌ๊ฐ€ ์“ฐ๋Š” ํฌ๋กฌ ๋ธŒ๋ผ์šฐ์ € ๊ฐ™์€ ์›น ํ”Œ๋žซํผ์—์„œ๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์ง€๋งŒ, jsdom์—์„œ๋Š” ๊ตฌํ˜„๋˜์ง€ ์•Š์€ ๊ฒƒ๋“ค์— ๋Œ€ํ•ด ์“ฐ์—ฌ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์›นํ”Œ๋žซํผ์—๋Š” ๊ธฐ๋Šฅ์ด ์žˆ์ง€๋งŒ, JSDOM์—๋Š” ์—†๋Š” ๊ฒƒ๋“ค

๐Ÿ™‡๐Ÿปโ€โ™€๏ธ ์ œ ๋‚˜๋ฆ„๋Œ€๋กœ ๋ฒˆ์—ญํ•˜๋ฉด์„œ ์ €์˜ ์–ธ์–ด๋กœ ์˜ฎ๊ฒจ์ ์€ ๊ฒƒ์ด๋‹ค๋ณด๋‹ˆ ์ •ํ™•ํ•˜๊ฒŒ ํ™•์ธํ•˜๊ณ  ์‹ถ์€ ๊ผผ๊ผผ์Ÿ์ด ๋ถ„๋“ค์€ ์ง์ ‘ ๋ฌธ์„œ๋ฅผ ์ฝ๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค ๐Ÿ‘

JSDOM ๊ฐ€ ์—ด์‹ฌํžˆ ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ์ง€๋งŒ ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ € API๋ฅผ ๋‹ค ๋ฐ˜์˜ํ•œ ์ƒํƒœ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ์›นํ”Œ๋žซํผ์—๋Š” ์žˆ์ง€๋งŒ JSDOM์— ์—†๋Š” ๊ฐ€์žฅ ๊ตต์งํ•œ 2๊ฐœ์˜ ๊ธฐ๋Šฅ์€ Navigation๊ณผ Layout์ž…๋‹ˆ๋‹ค.

  • Navigation: ๋งํฌ๋ฅผ ํด๋ฆญํ•˜๊ฑฐ๋‚˜ location.href ์— ๊ฐ’์„ ํ• ๋‹นํ•  ๋•Œ ์ „์—ญ๊ฐ์ฒด๋‚˜ ๊ด€๋ จ๋œ ๋‹ค๋ฅธ ๊ฐ์ฒด๋“ค์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด ์—†์Šต๋‹ˆ๋‹ค.
  • Layout: element๋“ค์ด CSS์˜ ๊ฒฐ๊ณผ๋กœ ์‹œ๊ฐ์ ์œผ๋กœ ๋ฐฐ์น˜๋˜์–ด ์žˆ๋Š” ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋Š” getBoundingClientRects() ๋˜๋Š” offsetTop ๊ฐ™์€ ์†์„ฑ์€ JSDOM์—์„œ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ƒํƒœ์˜ JSDOM์€ ์œ„ ๊ธฐ๋Šฅ๋“ค๊ณผ ๊ด€๋ จ๋˜์–ด์„œ๋Š” ๊ฐ€์ƒ(์‰ฝ๊ฒŒ ๋งํ•ด ๊ฐ€์งœ) ๋™์ž‘์„ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Naviagion ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ๊ฐ€์ƒ์˜ ์ฝ˜์†”์— not implemented(๊ตฌํ˜„๋˜์ง€ ์•Š์Œ)๋˜๋Š” jsdomError๊ฐ™์€ ์—๋Ÿฌ๋ฅผ ๋ฑ‰๊ณ , Laytout ๊ด€๋ จ ์†์„ฑ์ผ ๊ฒฝ์šฐ์—๋Š” 0์„ returnํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ scrollWidth, clientWidth๋Š” ํ•ญ์ƒ 0์„ returnํ•  ์ˆ˜ ๋ฐ–์— ์—†์Šต๋‹ˆ๋‹ค.

์–ด๋–ค ๊ฒฝ์šฐ์—๋Š” ์ฝ”๋“œ๋กœ ์ด๋Ÿฐ ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ํฌ๋กค๋ง ์ค‘์— ์ด๋™ํ•˜๋Š” ํŽ˜์ด์ง€๋งˆ๋‹ค JSDOM instance๋ฅผ ์ƒˆ๋กœ ์ƒ์„ฑํ•œ๋‹ค๋“ ์ง€, Object.defineProperty() ๋ฅผ ์ด์šฉํ•ด์„œ ๋‹ค์–‘ํ•œ ๋ ˆ์ด์•„์›ƒ๊ณผ ๊ด€๋ จํ•œ getter์™€ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฆฌํ„ดํ•˜๋Š” ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค๋ฉด ์ด๋Ÿฐ ์‹์œผ๋กœ์š”! viniciusavieira๋‹˜์ด ์ œ์•ˆํ•œ ๋‚ด์šฉ์„ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

const originalOffsetHeight =
Object.getOwnPropertyDescriptor(
HTMLElement.prototype,
'offsetHeight'
);
Object.defineProperty(
HTMLElement.prototype,
'offsetHeight',
originalOffsetHeight
)

๋˜๋Š” ์ผ„ํŠธ์”จ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์˜ˆ์ „์— ์‚ฌ์šฉํ•œ ์ ์ด ์žˆ๋‹ค๊ณ  ํ•˜๋„ค์š”. Cypress๋ฅผ ์“ฐ๊ฑฐ๋‚˜ ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์ด์šฉํ•  ๊ฒŒ ์•„๋‹ˆ๋ผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์ถ”์ฒœํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋” ๋งŽ์€ ์˜ˆ์ œ๋Š” ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import matchMediaPolyfill from 'mq-polyfill'
// ...

beforeAll(() => {
matchMediaPolyfill(window)
window.resizeTo = function resizeTo(width, height) {
Object.assign(this, {
innerWidth: width,
innerHeight: height,
outerWidth: width,
outerHeight: height,
}).dispatchEvent(new this.Event('resize'))
}
})
// ...then in my test
window.resizeTo(800, 300)

๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” JSDOM์ด ์ง€์›ํ•˜์ง€ ์•Š๊ณ  ์žˆ๋Š” ๊ธฐ๋Šฅ์„ PhantomJS๋Š” ์ง€์›ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, PhantomJS๋ฅผ ์ด์šฉํ•ด๋ณผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. PhantomJS์™€ JSDOM์„ ๋น„๊ตํ•˜๋Š” ๊ธ€์€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ PhantomJS๋Š” ์ข€ ์˜ค๋ž˜๋˜๊ณ  ํฌ๊ท€ํ•œ ๋ Œ๋”๋ง ์—”์ง„์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธดํ•˜์ง€๋งŒ ์™„์ „ํ•œ ๋ธŒ๋ผ์šฐ์ €์ด๊ณ , JSDOM์€ ์ˆœ์ˆ˜ํ•œ JavaScript๋กœ ๋งŒ๋“ค์–ด์ ธ Node.js ๊ธฐ๋ฐ˜์—์„œ ๋Œ์•„๊ฐ€๋Š” ์™„์ „ํ•œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์•„๋‹Œ ๋ผ์ดํŠธ๋ฒ„์ „์˜ ๋ธŒ๋ผ์šฐ์ €์ž…๋‹ˆ๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ๋Š” Layout์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋Š” Cypress๋กœ ํ•ด์•ผํ•œ๋‹ค

๊ฐ„๋‹จํ•œ ์œ ๋‹›ํ…Œ์ŠคํŠธ๋Š” ๋ถ„๋ช… testing-library/react๊ฐ€ ์œ ์šฉํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ Navigation(์ด ํŽ˜์ด์ง€์—์„œ ์ € ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•œ๋‹ค๋“ ์ง€..?)์ด๋‚˜ Layout์˜ ๊ฒฝ์šฐ์—๋Š” ์•„๋ฌด๋ž˜๋„ Cypress๊ฐ™์ด E2E ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๊ฒ ๊ตฌ๋‚˜! ๋ฅผ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.

JSDOM๋ฌธ์„œ์—์„œ๋Š” PhantomJS๊ฐ€ ์–ธ๊ธ‰๋˜์–ด ์žˆ์ง€๋งŒ.. ๋‚˜์ค‘์— E2E ํ…Œ์ŠคํŒ…์— ๋„์ „ํ•ด๋ณด๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•œ ๋ฒˆ์ฏค Cypress๋ฅผ ์ด์šฉํ•ด๋ณด๊ณ  ์‹ถ๋‹ค๋Š” ์ƒ๊ฐ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ.. Cypress๋กœ ๋ง์ค„์ž„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ์„œ ํ•ด๋ณด๋Š” ๊ฒƒ์€..
๋‹ค์Œ ๋ธ”๋กœ๊ทธ ๊ธ€์—์„œ ์‹œ๋„ํ•ด๋ณด๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค!
์ด๋ฒˆ ๋ธ”๋กœ๊น…์€ ์—ฌ๊ธฐ์„œ ๋—! ๐Ÿค“

--

--

FlyingSquirrel
FlyingSquirrel

Written by FlyingSquirrel

๊ฐ์„ฑ์ด ๋ง๋ž‘๋ง๋ž‘ํ•œ ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค.

No responses yet