μλ
νμΈμ, jsdom
μ΄λ―Έ URL
μμ±μλ₯Ό μ§μνμ§λ§ μ΄ 2κ°μ§ μ μ λ©μλκ° μλλΌ λλΆλΆμ μ΅μ λΈλΌμ°μ μμ μ§μλ©λλ€.
https://github.com/eligrey/Blob.js μ ꡬνμ jsdom
μμ΅λκΉ?
jspdf
λ΄μμ jsdom
jspdf
λ₯Ό μ¬μ©νμ¬ μ΄μ μν΄ μ°¨λ¨λμμ΅λλ€.
μλμ, μ΄ μ½λλ Blob URLμ΄ μλ λ°μ΄ν° URLμ μμ±νλ κ²μΌλ‘ 보μ λλ€. λκ΅°κ°λ https://w3c.github.io/FileAPI/#dfn -createObjectURLμμ μ¬μμ μ¬λ°λ₯΄κ² ꡬνν΄μΌ ν©λλ€.
μ΅μνμ§ μμλ° μ΄λ° μΌμ΄ μκΈΈκΉμ?
createObjectURL(blob) {
var implementationDefinedValue = ???
var url = `blob:${asciiSerialize(location.origin) || implementationDefinedValue}/${createUUID()}`;
saveToBlobStore(url, blob);
return url;
}
revokeObjectURL(blobUrl) {
// assume `getFromBlobStore()` will not throw
var blob = getFromBlobStore(blobUrl);
if (!blob) {
throw new NetworkError(...);
}
removeFromBlobStore(blobUrl);
}
@unional createUUID
λ uuidV4μΌ μ μμ΅λλ€
Chromeμ΄ μ¬μμ μ격νκ² λ°λ₯΄μ§ μλλ€κ³ μκ°ν©λλ€. λμΌν μΈμλ₯Ό λ λ² μ λ¬νκ±°λ μ«μλ λ°°μ΄κ³Ό κ°μ΄ μμνμ§ λͺ»ν μ νμ μ λ¬νλ©΄ URL.revokeObjectURL
NetworkError
λ°μνμ§ μμ΅λλ€.
νΈμ§νλ€:
Firefoxμμλ λμΌν μΌμ΄ λ°μν©λλ€. ꡬνμ λ¨μνκ² μ μ§νκ³ λΈλΌμ°μ μ λ§μ°¬κ°μ§λ‘ Blob μ μ₯μκ° νμνμ§ μμΌλ©° URL.revokeObjectURL
λ noop κΈ°λ₯μ΄ λ μ μμ΅λλ€.
κ·Έ μλ―Έλ:
const uuid = require('uuid/v4');
createObjectURL(blob) {
var implementationDefinedValue = ???
var url = `blob:${asciiSerialize(location.origin) || implementationDefinedValue}/${uuid()}`;
return url;
}
revokeObjectURL(blobUrl) {
return;
}
asciiSerialize(origin) {
if (origin.scheme) {
return `${origin.scheme}://${serializeHost(origin.host)}${origin.port? ':' + +origin.port : ''}`;
}
else {
return 'null';
}
}
serializeHost(host) {
...
}
μ΄λ―Έ μ½λ μ΄λκ°μ serializeHost()
μλ κ² κ°μ΅λλ€.
λ§μ§λ§μΌλ‘ ν κ°μ§λ λ€μκ³Ό κ°μ΅λλ€.
serializedκ° "null"μΈ κ²½μ° κ΅¬ν μ μ κ°μΌλ‘ μ€μ ν©λλ€.
"ꡬν μ μ κ°"μ 무μμ μλ―Έν©λκΉ?
λͺ¨λ μ§λ¬Έμ λν λ΅μ μ»μ κ² κ°μ΅λλ€.
http://stackoverflow.com/questions/42452543/what-does-implementation-defined-value-in-fileapi-unicodebloburl-mean/42452714#42452714
https://github.com/jsdom/whatwg-url μμ ꡬνν μ€λΉκ° λμλ€κ³ μκ°ν©λλ€.
μ
λ°μ΄νΈ: whatwg-url
μ¬μ© κ°λ₯ν λλΆλΆμ νλͺ©μ΄ μλ κ²μΌλ‘ λνλ¬μ΅λλ€. κ·Έλμ λλ μ½λκ° κ°λ¨νλ€κ³ μκ°ν©λλ€.
const uuid = require('uuid/v4');
createObjectURL(_blob) {
var url = `blob:${serializeURL(location.origin)}/${uuid()}`;
return url;
}
revokeObjectURL(_blobUrl) {
return;
}
κ·Έλ¬λ whatwg-url
κ° μ΄λ»κ² ꡬμ±λμ΄ μλμ§ λͺ¨λ₯΄κ² μ΅λλ€. κ·Έλμ μ½λλ₯Ό λ£λ λ°©λ²μ λͺ¨λ¦
λλ€.
@domenic , μμ μ½λκ° μ’μ 보μ΄κ³ κ±°κΈ°μ μΆκ°ν μ μμ΅λκΉ? μΆκ°ν μ μλμ?
Blob URLμ λ§λλ κ² μ΄μμ μνν΄μΌ ν©λλ€. μ΄νμ μ΄λ κ² μμ±λ URLμ΄ XMLHttpRequest
μ κ°μ μ¬μ©μμκ² μ 곡λλλ‘ νμ©ν΄μΌ ν©λλ€.
Blob URLμ λ§λλ κ² μ΄μμ μνν΄μΌ ν©λλ€. μ΄νμ μ΄λ κ² μμ±λ URLμ΄ XMLHttpRequestμ κ°μ μ¬μ©μμκ² μ 곡λλλ‘ νμ©ν΄μΌ ν©λλ€.
κ·Έκ²μ΄ νμν©λκΉ? μ¬μ© μ¬λ‘λ 무μμ
λκΉ? XMLHttpRequest
μ κΈ°μ‘΄ λ‘μ§μ΄ μ μ²λ¦¬λ κΉμ?
jsdom
μ λͺ©νκ° λΈλΌμ°μ κ° νλ μΌμ μ νν λμμ 볡μ νλ κ²μΈμ§ μ λͺ¨λ₯΄κ² μ΅λλ€.
κ·Έλ λ€λ©΄ μ°λ¦¬λ λΈλΌμ°μ λ₯Ό μμ±νκ³ μμ΅λλ€.
IMOμμλ μ€μ μ¬μ© μ¬λ‘κ° μμ λκΉμ§ μ΄λ₯Ό μ°κΈ°ν΄μΌ ββν©λλ€.
μ΄κ²μ μμΌλ‘ λμκ°κΈ° μν΄ μ°λ¦¬λ 무μμ λ ν μ μμ΅λκΉ?
var URL =
blob:${serializeURL(location.origin)}/${uuid()}
;
serializeURL()
λ μ¬μμ μ€λͺ
λ μκ³ λ¦¬μ¦λ³΄λ€ μ½κ° λ λ§μ μμ
μ μνν©λλ€.
μ°λ¦¬λ κ·Έκ²μ μ¬μ©νκ±°λ μ¬μμμ μ νν ꡬνν΄μΌ ν©λκΉ?
μ€μ λ‘ μλνμ§ μλ κ²½μ°(XHR, img μμ λ±μμ μ¬μ©λλ κ²½μ°) κ°μ²΄ URLμ jsdomμ μΆκ°νλ λ°μλ κ΄μ¬μ΄ μμ΅λλ€. κ·Έκ²λ€μ λ§λλ κ²λ§μΌλ‘λ λ§€μ° ν₯λ―Έλ‘μ§ μμ΅λλ€. λͺ μ€μ μ½λλ‘ μ΄λ₯Ό μνν μ μμ΅λλ€. μ΄λ₯Ό μν΄ μ 체 jsdom ꡬνμ΄ νμνμ§ μμ΅λλ€.
μ, κ·ΈλΌ κ΅¬ννλ €λ©΄ 무μμ΄ νμν κΉμ?
Nodeμμ DOM APIλ₯Ό μ¬μ©ν μ μκ² νλ κ² μΈμλ μλνλ ν μ€νΈλ₯Ό μ€ννκΈ° μν λΈλΌμ°μ κ° μμΌλ©΄ λ§€μ° μ μ©ν©λλ€.
κ·Έλ¬λ λ
Έλ μ¬μ©μ λν νΉμ μ¬μ© μ¬λ‘λ Blobμ΄ μμ±λλ λΈλΌμ°μ /λ
Έλ μ½λλ₯Ό μ€ννλ κ²½μ° λ νκ²½ λͺ¨λμμ μ΄ λ°©λ²μ μ¬μ©νμ¬ λ΄μ©μ κ²μ¬ν μ μλ€λ κ²μ
λλ€. μ΄κ²). (λ΄ κ²½μ°μλ IndexedDB(λ° postMessage
)μμ μ¬μ©νλ ꡬ쑰νλ 볡μ μκ³ λ¦¬μ¦μμ Blob 볡μ λ₯Ό μν λ
Έλλ³ μ½λλ₯Ό μμ±νμ§ μμλ λ©λλ€.)
Blob URLμ μλ²μ λ
립μ μΈ λ©λͺ¨λ¦¬μμ μμ
ν΄μΌ νλ νΉλ³ν μꡬ μ¬νμ μΆ©μ‘±ν©λλ€. data:
URLλ³΄λ€ Blob URLμλ μΈ κ°μ§ κ³ μ ν μΈ‘λ©΄μ΄ μλ€κ³ μκ°ν©λλ€(μ΄μ€μΌμ΄ν μμ΄ λ μ½κ² ꡬμ±ν μ μλ€λ μ μΈμλ).
νλλ λ©λͺ¨λ¦¬ λ΄ μ½ν μΈ λ₯Ό λμ΄ URL λ΄ μ 체 νμΌ μ½ν μΈ λ₯Ό μΈμ½λ©ν νμ μμ΄ νμΌμ μ°Έμ‘°ν μ μλ€λ κ²μ λλ€(μ¬μ©ν λκΉμ§ μμ°Έμ‘°ν νμκ° μμ).
λ λ²μ§Έλ λΈλΌμ°μ μΈμ λλ μ¬μ©μ λΈλΌμ°μ μΈλΆμμ λ°μ΄ν°κ° μ§μλλ κ²μ λ°©μ§νλ 보μ/κ°μΈ μ 보 보νΈμ μ μ©ν μ μλ€λ κ²μ λλ€.
μΈ λ²μ§Έλ Blob URLμ΄ μΈμ
λ΄μμλ μ·¨μλ μ μμ΄ μ°Έμ‘°κ° λ§λ£λ μ μλ€λ κ²μ
λλ€(μ: μ΄λ―Έμ§λ₯Ό λ§λ€κ³ Blob URLμ ν΅ν΄ νμν λ€μ μ¬μ©μκ° μ΄λ―Έμ§λ₯Ό μμ νλ©΄ Blobμ΄ μμ λλλ‘ URLμ μ·¨μν μ μμ΅λλ€. URLμ data
URLκ³Ό λ¬λ¦¬ μ¬μ¬μ©ν μ μμ΅λλ€.
μ€μ λ‘ μλνμ§ μλ κ²½μ°(XHR, img μμ λ±μμ μ¬μ©λλ κ²½μ°) κ°μ²΄ URLμ jsdomμ μΆκ°νλ λ°μλ κ΄μ¬μ΄ μμ΅λλ€. κ·Έκ²λ€μ λ§λλ κ²λ§μΌλ‘λ λ§€μ° ν₯λ―Έλ‘μ§ μμ΅λλ€.
react-snapshot μ λμμΌλ‘ React(CRA) νλ‘μ νΈμ μλ² μΈ‘ λ λλ§μ νκ³ μΆμ΅λλ€. λ΄ νλ‘μ νΈλ λν webworkify λ₯Ό μ¬μ©νλ mapbox-glμ μ¬μ©νλ react-mapbox-glμ μ¬μ© ν©λλ€ . κ·Έλ¦¬κ³ νλ‘μΈμ€κ° μ€ν¨νκΈ° λλ¬Έμ
Error: Uncaught [TypeError: o.URL.createObjectURL is not a function]
μ€μ λ‘ μλ²μμ 맡μ λ λλ§νλ λ° κ΄μ¬μ΄ μμ΅λλ€. μλ²μμ 맡 λμ μ리 νμμλ₯Ό λ λλ§νκ³ μΆμ§λ§ μ΄ μ€λ₯λ‘ μΈν΄ μ΄λ₯Ό λ°©μ§ν μ μμ΅λλ€.
λͺ μ€μ μ½λλ‘ μ΄λ₯Ό μνν μ μμ΅λλ€. μ΄λ₯Ό μν΄ μ 체 jsdom ꡬνμ΄ νμνμ§ μμ΅λλ€.
μ μ΄λ μ΄κ²μ λν noop κΈ°λ₯μ μΆκ°νκΈ° μν΄ jsdomμ ν¨μΉν μ μλ λ°©λ²μ΄ μμ΅λκΉ? λλ κ·Έκ²μ΄ μ€ν¨ν κ²μ΄λΌκ³ μκ°νμ§λ§ μ΄κ²μ΄ μ μ΄λ μΆλ°μ μ΄ λ κ²μ λλ€.
μ½μ΄λ³΄κΈ°: https://github.com/tmpvar/jsdom#intervening -before-parsing
κ°μ¬ ν΄μ. λλ μ΄κ²μ κ°μ§κ³ μμ§ μμ jsdom.env
μ κ°μ μ€λλ APIμ λΆμ΄ μμ΄ μ κ°μ
λλ€.
options.beforeParse(this[window]._globalProxy);
(facepalm) μ’ λ νλ€κ² λ€
UPD
created
μ½λ°±μ΄ μμ΅λλ€
created: (err, window) => {
window.URL = { createObjectURL: () => {} }
}
μ΄ κΈ°λ₯ μμ²μ μνλ 무μμ λκΉ? νμ€ν μμΌλ©΄ μ μ©ν κ²μ λλ€.
@fredvollmer jsdomκ³Ό ν¨κ» jestλ₯Ό μ¬μ©νκ³ μ΄ κΈ°λ₯μ μ¬μ©νμ§ μμΌλ €λ©΄ μ΄ μ½λλ₯Ό setupTest.js
(λλ μ΄μ μμνλ μ€μ μ€ν¬λ¦½νΈ)μ μΆκ°ν μ μμ΅λλ€.
function noOp () { }
if (typeof window.URL.createObjectURL === 'undefined') {
Object.defineProperty(window.URL, 'createObjectURL', { value: noOp})
}
@dylanjha λλ TypeError: Cannot redefine property: createObjectURL
μ»μ΅λλ€
@franciscolourenco λ§μ΅λλ€! μ£μ‘ν©λλ€... μμ μμ μμ μ½λλ₯Ό μ
λ°μ΄νΈνμ¬ μ΄ μ‘°κ±΄λ¬Έμ λννμΌλ©° μ΄κ²μ΄ μ μκ² ν¨κ³Όμ μ
λλ€. if (typeof window.URL.createObjectURL === 'undefined') {
μλν΄λ³΄μμμ€!
μ¬κΈ°λ‘ λμμ μμ λ₯Ό μ λ°μ΄νΈνλλ‘ μκΈ°μμΌ μ£Όμ μ κ°μ¬ν©λλ€.
@dylanjha createObjectURL
μ΄ μ μλμμ§λ§ ν¨μκ° μλλ―λ‘ μΌλΆ λΌμ΄λΈλ¬λ¦¬μμ μ€λ₯κ° λ°μν©λλ€. λλ₯Ό μν΄ μΌν κ³Όμ :
window.URL.createObjectURL = noOp
μ΄κ²μ΄ κΏκΊ½κΏκΊ½μλ μ μ©λ μ μλμ§ μλ μ¬λμ΄ μμ΅λκΉ? κΈ°λ³Έμ μΌλ‘ uncssκ° μ¬μ©νλ jsdomμμ λ°μνλ createObjectURL
μ λν jsdom μ€λ₯λ₯Ό 침묡μν€κ³ μΆμ΅λλ€.
미리 κ°μ¬λ립λλ€!
if (typeof URL !== 'undefined') {
delete URL;
}
λ λͺ ννκ²νκΈ° μν΄ λ€μκ³Ό κ°μ μμ μ μνν©λλ€.
function noOp () { }
if (typeof window.URL.createObjectURL === 'undefined') {
Object.defineProperty(window.URL, 'createObjectURL', { value: noOp})
}
jest ꡬμ±μ "setupFiles" νμΌμ λ£μ κ²½μ° μλ§ μλν©λλ€. κ·Έλμ λΉμ μ package.json λλ λΉμ μ΄ κ°μ§κ³ μλ λͺ¨λ κ³³μμ
"jest": {
"setupFiles": [
"<rootDir>/internals/testing/setup_file.js"
],
}
λ€λ₯Έ μ¬λμ΄ μμ±ν μ½λλ₯Ό κ·Έ νμΌ μμ λ£μ΅λλ€. (ν΄λΉ νμΌμ΄ μ‘΄μ¬νμ§ μλλ€λ©΄ μμ±ν΄μΌ ν μλ μμ΅λλ€.)
μ΄κ²μ΄ μ루μ μ λν΄ νΌλμ€λ¬μνλ μ¬λμκ² λμμ΄λκΈ°λ₯Ό λ°λλλ€.
img.src = URL.createObjectURL(λΈλ‘);
μλνμ§ μμ, μ€λ₯ λ°μ URL.createObjectURLμ jest ν
μ€νΈ μΌμ΄μ€ νμΌμ 체ν¬μΈνλ λμ
μ΄λ―Έμ§ srcμ λ°νλ blobμ ν λΉ νλ
μ΄κ²μ ν΄ν€νκ³ κΆμ₯νμ§ μμ§λ§ Node.jsμ λν΄ createObjectURL
λ° revokeObjectURL
μ λ¨μνλ
μ¬μ©νκΈ° μ μ 무μμ νκ³ μλμ§ νμΈνμμμ€.
κ°μ₯ μ μ©ν λκΈ
@fredvollmer jsdomκ³Ό ν¨κ» jestλ₯Ό μ¬μ©νκ³ μ΄ κΈ°λ₯μ μ¬μ©νμ§ μμΌλ €λ©΄ μ΄ μ½λλ₯Ό
setupTest.js
(λλ μ΄μ μμνλ μ€μ μ€ν¬λ¦½νΈ)μ μΆκ°ν μ μμ΅λλ€.