Replace XMLHttpRequest (#562)

* Replace XMLHttpRequest with fetch

* Implement fetch placeholder for tests
This commit is contained in:
toasted-nutbread 2020-06-13 10:23:04 -04:00 committed by GitHub
parent 5cba421201
commit 8a7ff6a18c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 106 deletions

View File

@ -82,16 +82,24 @@ class AudioUriBuilder {
}
async _getUriJpod101Alternate(definition) {
const response = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.addEventListener('error', () => reject(new Error('Failed to scrape audio data')));
xhr.addEventListener('load', () => resolve(xhr.responseText));
xhr.send(`post=dictionary_reference&match_type=exact&search_query=${encodeURIComponent(definition.expression)}`);
const fetchUrl = 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post';
const data = `post=dictionary_reference&match_type=exact&search_query=${encodeURIComponent(definition.expression)}`;
const response = await fetch(fetchUrl, {
method: 'POST',
mode: 'no-cors',
cache: 'default',
credentials: 'omit',
redirect: 'follow',
referrerPolicy: 'no-referrer',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: data
});
const responseText = await response.text();
console.log(responseText);
const dom = new DOMParser().parseFromString(response, 'text/html');
const dom = new DOMParser().parseFromString(responseText, 'text/html');
for (const row of dom.getElementsByClassName('dc-result-row')) {
try {
const url = row.querySelector('audio>source[src]').getAttribute('src');
@ -108,15 +116,18 @@ class AudioUriBuilder {
}
async _getUriJisho(definition) {
const response = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', `https://jisho.org/search/${definition.expression}`);
xhr.addEventListener('error', () => reject(new Error('Failed to scrape audio data')));
xhr.addEventListener('load', () => resolve(xhr.responseText));
xhr.send();
const fetchUrl = `https://jisho.org/search/${definition.expression}`;
const response = await fetch(fetchUrl, {
method: 'GET',
mode: 'no-cors',
cache: 'default',
credentials: 'omit',
redirect: 'follow',
referrerPolicy: 'no-referrer'
});
const responseText = await response.text();
const dom = new DOMParser().parseFromString(response, 'text/html');
const dom = new DOMParser().parseFromString(responseText, 'text/html');
try {
const audio = dom.getElementById(`audio_${definition.expression}:${definition.reading}`);
if (audio !== null) {

View File

@ -16,28 +16,28 @@
*/
function requestText(url, action, params) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.overrideMimeType('text/plain');
xhr.addEventListener('load', () => resolve(xhr.responseText));
xhr.addEventListener('error', () => reject(new Error('Failed to connect')));
xhr.open(action, url);
if (params) {
xhr.send(JSON.stringify(params));
} else {
xhr.send();
}
async function requestText(url, method, data) {
const response = await fetch(url, {
method,
mode: 'no-cors',
cache: 'default',
credentials: 'omit',
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: (data ? JSON.stringify(data) : void 0)
});
return await response.text();
}
async function requestJson(url, action, params) {
const responseText = await requestText(url, action, params);
try {
return JSON.parse(responseText);
} catch (e) {
const error = new Error(`Invalid response (${e.message || e})`);
error.data = {url, action, params, responseText};
throw error;
}
async function requestJson(url, method, data) {
const response = await fetch(url, {
method,
mode: 'no-cors',
cache: 'default',
credentials: 'omit',
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: (data ? JSON.stringify(data) : void 0)
});
return await response.json();
}

View File

@ -169,22 +169,22 @@ class AudioSystem {
});
}
_createAudioBinaryFromUrl(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.addEventListener('load', async () => {
const arrayBuffer = xhr.response;
if (!await this._isAudioBinaryValid(arrayBuffer)) {
reject(new Error('Could not retrieve audio'));
} else {
resolve(arrayBuffer);
}
});
xhr.addEventListener('error', () => reject(new Error('Failed to connect')));
xhr.open('GET', url);
xhr.send();
async _createAudioBinaryFromUrl(url) {
const response = await fetch(url, {
method: 'GET',
mode: 'no-cors',
cache: 'default',
credentials: 'omit',
redirect: 'follow',
referrerPolicy: 'no-referrer'
});
const arrayBuffer = await response.arrayBuffer();
if (!await this._isAudioBinaryValid(arrayBuffer)) {
throw new Error('Could not retrieve audio');
}
return arrayBuffer;
}
_isAudioValid(audio) {

View File

@ -38,60 +38,6 @@ const chrome = {
}
};
class XMLHttpRequest {
constructor() {
this._eventCallbacks = new Map();
this._url = '';
this._responseText = null;
}
overrideMimeType() {
// NOP
}
addEventListener(eventName, callback) {
let callbacks = this._eventCallbacks.get(eventName);
if (typeof callbacks === 'undefined') {
callbacks = [];
this._eventCallbacks.set(eventName, callbacks);
}
callbacks.push(callback);
}
open(action, url2) {
this._url = url2;
}
send() {
const filePath = url.fileURLToPath(this._url);
Promise.resolve()
.then(() => {
let source;
try {
source = fs.readFileSync(filePath, {encoding: 'utf8'});
} catch (e) {
this._trigger('error');
return;
}
this._responseText = source;
this._trigger('load');
});
}
get responseText() {
return this._responseText;
}
_trigger(eventName, ...args) {
const callbacks = this._eventCallbacks.get(eventName);
if (typeof callbacks === 'undefined') { return; }
for (let i = 0, ii = callbacks.length; i < ii; ++i) {
callbacks[i](...args);
}
}
}
class Image {
constructor() {
this._src = '';
@ -138,11 +84,21 @@ class Image {
}
}
async function fetch(url2) {
const filePath = url.fileURLToPath(url2);
await Promise.resolve();
const content = fs.readFileSync(filePath, {encoding: null});
return {
text: async () => Promise.resolve(content.toString('utf8')),
json: async () => Promise.resolve(JSON.parse(content.toString('utf8')))
};
}
const vm = new VM({
chrome,
Image,
XMLHttpRequest,
fetch,
indexedDB: global.indexedDB,
IDBKeyRange: global.IDBKeyRange,
JSZip: yomichanTest.JSZip,