Comparing version 9.5.0 to 9.6.0
@@ -416,3 +416,3 @@ "use strict"; | ||
namespace: sourceAttr._namespace, | ||
prefix: sourceAttr._prefix, | ||
namespacePrefix: sourceAttr._namespacePrefix, | ||
localName: sourceAttr._localName, | ||
@@ -419,0 +419,0 @@ value: sourceAttr._value |
@@ -56,3 +56,3 @@ "use strict"; | ||
get lastModified() { | ||
return this[blobSymbols.lastModified]; | ||
return this[blobSymbols.lastModified] || Date.now(); | ||
} | ||
@@ -59,0 +59,0 @@ slice() { |
@@ -150,2 +150,4 @@ "use strict"; | ||
eventImpl._dispatchFlag = false; | ||
eventImpl._stopPropagationFlag = false; | ||
eventImpl._stopImmediatePropagationFlag = false; | ||
eventImpl.eventPhase = Event.NONE; | ||
@@ -152,0 +154,0 @@ eventImpl.currentTarget = null; |
@@ -44,2 +44,4 @@ "use strict"; | ||
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; | ||
function updateHTMLCollection(collection) { | ||
@@ -46,0 +48,0 @@ if (collection[privates].version < collection[privates].element._version) { |
@@ -30,2 +30,14 @@ "use strict"; | ||
function updateRequestHeader(requestHeaders, header, newValue) { | ||
const lcHeader = header.toLowerCase(); | ||
const keys = Object.keys(requestHeaders); | ||
let n = keys.length; | ||
while (n--) { | ||
const key = keys[n]; | ||
if (key.toLowerCase() === lcHeader) { | ||
requestHeaders[key] = newValue; | ||
} | ||
} | ||
} | ||
const simpleMethods = new Set(["GET", "HEAD", "POST"]); | ||
@@ -35,2 +47,3 @@ const simpleHeaders = new Set(["accept", "accept-language", "content-language", "content-type"]); | ||
exports.getRequestHeader = getRequestHeader; | ||
exports.updateRequestHeader = updateRequestHeader; | ||
exports.simpleHeaders = simpleHeaders; | ||
@@ -152,12 +165,12 @@ | ||
if (!getRequestHeader(flag.requestHeaders, "referer")) { | ||
if (getRequestHeader(flag.requestHeaders, "referer") === null) { | ||
requestHeaders.Referer = flag.referrer; | ||
} | ||
if (!getRequestHeader(flag.requestHeaders, "user-agent")) { | ||
if (getRequestHeader(flag.requestHeaders, "user-agent") === null) { | ||
requestHeaders["User-Agent"] = flag.userAgent; | ||
} | ||
if (!getRequestHeader(flag.requestHeaders, "accept-language")) { | ||
if (getRequestHeader(flag.requestHeaders, "accept-language") === null) { | ||
requestHeaders["Accept-Language"] = "en"; | ||
} | ||
if (!getRequestHeader(flag.requestHeaders, "accept")) { | ||
if (getRequestHeader(flag.requestHeaders, "accept") === null) { | ||
requestHeaders.Accept = "*/*"; | ||
@@ -208,3 +221,3 @@ } | ||
if (hasBody && !getRequestHeader(flag.requestHeaders, "content-type")) { | ||
if (hasBody && getRequestHeader(flag.requestHeaders, "content-type") === null) { | ||
requestHeaders["Content-Type"] = "text/plain;charset=UTF-8"; | ||
@@ -211,0 +224,0 @@ } |
@@ -509,2 +509,4 @@ "use strict"; | ||
!(flag.method === "HEAD" || flag.method === "GET")) { | ||
let contentType = null; | ||
let encoding = null; | ||
if (body instanceof FormData) { | ||
@@ -532,4 +534,8 @@ flag.formData = true; | ||
flag.body = formData; | ||
// TODO content type; what is the form boundary? | ||
} else if (body instanceof Blob) { | ||
flag.body = body[blobSymbols.buffer]; | ||
if (body[blobSymbols.type] !== "") { | ||
contentType = body[blobSymbols.type]; | ||
} | ||
} else if (body instanceof ArrayBuffer) { | ||
@@ -542,3 +548,8 @@ flag.body = new Buffer(new Uint8Array(body)); | ||
flag.body = domToHtml([body]); | ||
flag.requestHeaders["Content-Type"] = body.contentType + ";charset=UTF-8"; | ||
encoding = "UTF-8"; | ||
const documentBodyParsingMode = idlUtils.implForWrapper(body)._parsingMode; | ||
contentType = documentBodyParsingMode === "html" ? "text/html" : "application/xml"; | ||
contentType += ";charset=UTF-8"; | ||
} else if (typeof body !== "string") { | ||
@@ -548,3 +559,20 @@ flag.body = String(body); | ||
flag.body = body; | ||
contentType = "text/plain;charset=UTF-8"; | ||
encoding = "UTF-8"; | ||
} | ||
const existingContentType = xhrUtils.getRequestHeader(flag.requestHeaders, "content-type"); | ||
if (contentType !== null && existingContentType === null) { | ||
flag.requestHeaders["Content-Type"] = contentType; | ||
} else if (existingContentType !== null && encoding !== null) { | ||
const parsed = parseContentType(existingContentType); | ||
if (parsed) { | ||
parsed.parameterList | ||
.filter(v => v.key && v.key.toLowerCase() === "charset" && normalizeEncoding(v.value) !== "UTF-8") | ||
.forEach(v => { | ||
v.value = "UTF-8"; | ||
}); | ||
xhrUtils.updateRequestHeader(flag.requestHeaders, "content-type", parsed.toString()); | ||
} | ||
} | ||
} | ||
@@ -702,2 +730,3 @@ } finally { | ||
const properties = this[xhrSymbols.properties]; | ||
if (arguments.length !== 2) { | ||
@@ -708,5 +737,3 @@ throw new TypeError("2 arguments required for setRequestHeader"); | ||
value = toByteString(value); | ||
if (!tokenRegexp.test(header) || !fieldValueRegexp.test(value)) { | ||
throw new DOMException(DOMException.SYNTAX_ERR); | ||
} | ||
if (this.readyState !== XMLHttpRequest.OPENED || properties.send) { | ||
@@ -716,2 +743,8 @@ throw new DOMException(DOMException.INVALID_STATE_ERR); | ||
value = normalizeHeaderValue(value); | ||
if (!tokenRegexp.test(header) || !fieldValueRegexp.test(value)) { | ||
throw new DOMException(DOMException.SYNTAX_ERR); | ||
} | ||
const lcHeader = header.toLowerCase(); | ||
@@ -723,14 +756,2 @@ | ||
if (lcHeader === "content-type") { | ||
const contentType = parseContentType(value); | ||
if (contentType) { | ||
contentType.parameterList | ||
.filter(v => v.key && v.key.toLowerCase() === "charset" && normalizeEncoding(v.value) !== "UTF-8") | ||
.forEach(v => { | ||
v.value = "UTF-8"; | ||
}); | ||
value = contentType.toString(); | ||
} | ||
} | ||
const keys = Object.keys(flag.requestHeaders); | ||
@@ -741,7 +762,7 @@ let n = keys.length; | ||
if (key.toLowerCase() === lcHeader) { | ||
flag.requestHeaders[key] += ", " + value; | ||
flag.requestHeaders[key] += "," + value; | ||
return; | ||
} | ||
} | ||
flag.requestHeaders[header] = value; | ||
flag.requestHeaders[lcHeader] = value; | ||
} | ||
@@ -875,2 +896,3 @@ | ||
readyStateChange(xhr, XMLHttpRequest.DONE); | ||
xhr.dispatchEvent(new ProgressEvent("progress", progressObj)); | ||
xhr.dispatchEvent(new ProgressEvent("load", progressObj)); | ||
@@ -886,16 +908,17 @@ xhr.dispatchEvent(new ProgressEvent("loadend", progressObj)); | ||
let total = 0; | ||
let lengthComputable = false; | ||
const length = parseInt(xhrUtils.getRequestHeader(client.headers, "content-length")); | ||
if (length) { | ||
total = length; | ||
lengthComputable = true; | ||
} | ||
const initProgress = { | ||
lengthComputable, | ||
total, | ||
loaded: 0 | ||
}; | ||
upload.dispatchEvent(new ProgressEvent("loadstart", initProgress)); | ||
client.on("request", req => { | ||
let total = 0; | ||
let lengthComputable = false; | ||
const length = parseInt(xhrUtils.getRequestHeader(client.headers, "content-length")); | ||
if (length) { | ||
total = length; | ||
lengthComputable = true; | ||
} | ||
const initProgress = { | ||
lengthComputable, | ||
total, | ||
loaded: 0 | ||
}; | ||
upload.dispatchEvent(new ProgressEvent("loadstart", initProgress)); | ||
req.on("response", () => { | ||
@@ -991,3 +1014,7 @@ properties.uploadComplete = true; | ||
function normalizeHeaderValue(value) { | ||
return value.replace(/^[\x09\x0A\x0D\x20]+/, "").replace(/[\x09\x0A\x0D\x20]+$/, ""); | ||
} | ||
return XMLHttpRequest; | ||
}; |
{ | ||
"name": "jsdom", | ||
"version": "9.5.0", | ||
"version": "9.6.0", | ||
"description": "A JavaScript implementation of the DOM and HTML standards", | ||
@@ -64,3 +64,3 @@ "keywords": [ | ||
"wd": "0.3.12", | ||
"webidl2js": "^4.3.0" | ||
"webidl2js": "^5.0.0" | ||
}, | ||
@@ -67,0 +67,0 @@ "browser": { |
@@ -562,2 +562,18 @@ # jsdom | ||
## jsdom vs. PhantomJS | ||
Some people wonder what the differences are between jsdom and [PhantomJS](http://phantomjs.org/), and when you would use one over the other. Here we attempt to explain some of the differences, and why we find jsdom to be a pleasure to use for testing and scraping use cases. | ||
PhantomJS is a complete browser (although it uses a very old and rare rendering engine). It even performs layout and rendering, allowing you to query element positions or take a screenshot. jsdom is not a full browser: it does not perform layout or rendering, and it does not support navigation between pages. It _does_ support the DOM, HTML, canvas, many other web platform APIs, and running scripts. | ||
So you could use jsdom to fetch the HTML of your web application (while also executing the JavaScript code within that HTML). And then you could examine and modify the resulting DOM tree. Or you could trigger event listeners to test how the web application reacts. You could also use jsdom to build up your own DOM tree from scratch, and then serialize it to a HTML string. | ||
You need an executable to run PhantomJS. It is written in native code, and has to be compiled for each platform. jsdom is pure JavaScript, and runs wherever Node.js runs. It even has experimental support for running within browsers, giving you the ability to create a whole DOM Document inside a web worker. | ||
One of the reasons jsdom is used a lot for testing is that creating a new document instance has very little overhead in jsdom. Opening a new page in PhantomJS takes a lot of time, so running a lot of small tests in fresh documents could take minutes in PhantomJS, but only seconds in jsdom. | ||
Another important benefit jsdom has for testing is a bit more complicated: it is easy to suffer race conditions using an external process like PhantomJS (or Selenium). For example if you create a script to test something using PhantomJS, that script will live in a different process than the web application. If you perform multiple steps in your test that are dependent on each other (for example, step 1: find the element; step 2: click on the element), the application might change the DOM during those steps (step 2.5: the page's JavaScript removes the element). This is not an issue in jsdom, since your tests live in exactly the same thread and event loop as the web application, so if your test is executing JavaScript code, the web application cannot run its code until your test releases control of the event loop. | ||
In general the same reasons that make jsdom pleasant for testing also make it pleasant for web scraping. In both cases, the extra power of a full browser is not as important as getting things done easily and quickly. | ||
## What Standards Does jsdom Support, Exactly? | ||
@@ -564,0 +580,0 @@ |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1168331
33560
593