archive-monorepo/@tornado/xhr2-cookies/test/events.spec.ts
T-Hax 6006120e60
Set up monorepo
Signed-off-by: T-Hax <>
2023-05-03 20:35:27 +00:00

250 lines
7.8 KiB
TypeScript

import * as ava from 'ava';
import { XMLHttpRequest } from '../xml-http-request';
import { HttpServer } from './helpers/server';
function contextualize<T>(getContext: () => T): ava.RegisterContextual<T> {
ava.test.beforeEach(t => {
Object.assign(t.context, getContext());
});
return ava.test;
}
const test = contextualize(() => ({
xhr: new XMLHttpRequest(),
dripUrl: `http://localhost:${HttpServer.port}/_/drip`,
dripJson: {drips: 3, size: 1000, ms: 50, length: true},
}));
test.before(async () => {
await HttpServer.serverStarted;
XMLHttpRequest.nodejsSet({
baseUrl: HttpServer.testUrl().replace('https://', 'http://')
});
});
test.beforeEach(t => {
t.context.xhr = new XMLHttpRequest();
});
test('level 2 events for a successful fetch with Content-Length set', async t => {
let endFired = false;
let intermediateProgressFired = false;
const xhr = t.context.xhr;
await new Promise(resolve => {
['loadstart', 'progress', 'load', 'loadend', 'error', 'abort'].forEach(addCheckedEvent);
xhr.addEventListener('loadend', () => {
endFired = true;
resolve();
});
xhr.addEventListener('error', () => resolve());
xhr.open('POST', t.context.dripUrl);
xhr.send(JSON.stringify(t.context.dripJson));
});
t.true(intermediateProgressFired, 'at least one intermediate progress event was fired');
function addCheckedEvent(eventType: string) {
xhr.addEventListener(eventType, event => {
t.is(event.type, eventType, `event type is ${eventType}`);
t.is(event.target, xhr, 'event has correct target');
t.false(endFired, 'end is not fired');
t.false(event.bubbles, 'event does not bubble');
t.false(event.cancelable, 'event is not cancelable');
switch (eventType) {
case 'loadstart':
t.is(event.loaded, 0, 'on loadstart loaded = 0');
t.false(event.lengthComputable, 'on loadstart length is not computable');
t.is(event.total, 0, 'on loadstart event total is 0');
break;
case 'load':
case 'loadend':
t.is(event.loaded, 3000, 'on load/loadend loaded = 3000');
t.true(event.lengthComputable, 'on load/loadend length is computable');
t.is(event.total, 3000, 'on load/loadend event total is 0');
break;
case 'progress':
t.true(event.loaded >= 0, 'on progress: loaded >= 0');
t.true(event.loaded <= 3000, 'on progress: loaded <= 3000');
if (event.lengthComputable) {
t.is(event.total, 3000, 'on progress event when length is computable total is 3000');
} else {
t.is(event.total, 0, 'on progress event when length is not computable total is 0');
}
if (event.loaded > 0 && event.loaded < 3000) { intermediateProgressFired = true; }
break;
}
})
}
});
test('level 2 events for a successful fetch without Content-Length set', async t => {
let endFired = false;
let intermediateProgressFired = false;
const xhr = t.context.xhr;
t.context.dripJson = {...t.context.dripJson, length: false};
await new Promise(resolve => {
['loadstart', 'progress', 'load', 'loadend', 'error', 'abort'].forEach(addCheckedEvent);
xhr.addEventListener('loadend', () => {
endFired = true;
resolve();
});
xhr.open('POST', t.context.dripUrl);
xhr.send(JSON.stringify(t.context.dripJson));
});
t.true(intermediateProgressFired, 'at least one intermediate progress event was fired');
function addCheckedEvent(eventType: string) {
xhr.addEventListener(eventType, event => {
t.is(event.type, eventType, `event type is ${eventType}`);
t.is(event.target, xhr, 'event has correct target');
t.false(endFired, 'end is not fired');
t.false(event.bubbles, 'event does not bubble');
t.false(event.cancelable, 'event is not cancelable');
t.false(event.lengthComputable, 'length is not computable');
t.is(event.total, 0, 'when length is not computable total is 0');
switch (eventType) {
case 'loadstart':
t.is(event.loaded, 0, 'on loadstart loaded = 0');
break;
case 'load':
case 'loadend':
t.is(event.loaded, 3000, 'on load/loadend loaded = 3000');
break;
case 'progress':
t.true(event.loaded >= 0, 'on progress: loaded >= 0');
t.true(event.loaded <= 3000, 'on progress: loaded <= 3000');
if (event.loaded > 0 && event.loaded < 3000) { intermediateProgressFired = true; }
break;
}
})
}
});
test('level 2 events for a network error due to bad DNS', async t => {
let errorFired = false;
const xhr = t.context.xhr;
await new Promise(resolve => {
['loadstart', 'progress', 'load', 'loadend', 'error', 'abort'].forEach(addCheckedEvent);
xhr.addEventListener('loadend', () => resolve());
xhr.open('GET', 'https://broken.to.cause.an.xhrnetworkerror.com.a.com');
xhr.send();
});
t.true(errorFired, 'an error event was fired');
function addCheckedEvent(eventType: string) {
xhr.addEventListener(eventType, () => {
switch (eventType) {
case 'load':
case 'progress':
t.fail();
break;
case 'error':
errorFired = true;
break;
}
})
}
});
test('readystatechange for a successful fetch with Content-Length set', async t => {
let doneFired = false;
const xhr = t.context.xhr;
const states = [];
await new Promise(resolve => {
xhr.addEventListener('readystatechange', event => {
t.is(event.type, 'readystatechange', 'event type is correct');
t.false(doneFired, 'no readystatechange events after DONE');
t.is(event.target, xhr, 'event has correct target');
t.false(event.bubbles, 'event does not bubble');
t.false(event.cancelable, 'event is not cancelable');
states.push((event.target as XMLHttpRequest).readyState);
if ((event.target as XMLHttpRequest).readyState === XMLHttpRequest.DONE) {
doneFired = true;
resolve();
}
});
xhr.open('POST', t.context.dripUrl);
xhr.send(JSON.stringify(t.context.dripJson));
});
t.deepEqual(states, [
XMLHttpRequest.OPENED,
XMLHttpRequest.HEADERS_RECEIVED,
XMLHttpRequest.LOADING,
XMLHttpRequest.DONE
], 'right order of ready states');
});
test('readystatechange for a successful fetch without Content-Length set', async t => {
let doneFired = false;
const xhr = t.context.xhr;
const states = [];
t.context.dripJson = {...t.context.dripJson, length: false};
await new Promise(resolve => {
xhr.addEventListener('readystatechange', event => {
t.is(event.type, 'readystatechange', 'event type is correct');
t.false(doneFired, 'no readystatechange events after DONE');
t.is(event.target, xhr, 'event has correct target');
t.false(event.bubbles, 'event does not bubble');
t.false(event.cancelable, 'event is not cancelable');
t.false(event.lengthComputable, 'length is not computable');
t.is(event.total, 0, 'when length is not computable total is 0');
states.push((event.target as XMLHttpRequest).readyState);
if ((event.target as XMLHttpRequest).readyState === XMLHttpRequest.DONE) {
doneFired = true;
resolve();
}
});
xhr.open('POST', t.context.dripUrl);
xhr.send(JSON.stringify(t.context.dripJson));
});
t.deepEqual(states, [
XMLHttpRequest.OPENED,
XMLHttpRequest.HEADERS_RECEIVED,
XMLHttpRequest.LOADING,
XMLHttpRequest.DONE
], 'right order of ready states');
});
test('readystatechange for a network error due to bad DNS', async t => {
const xhr = t.context.xhr;
const states = [];
await new Promise(resolve => {
xhr.addEventListener('readystatechange', event => {
t.is(event.type, 'readystatechange', 'event type is correct');
t.is(event.target, xhr, 'event has correct target');
t.false(event.bubbles, 'event does not bubble');
t.false(event.cancelable, 'event is not cancelable');
states.push((event.target as XMLHttpRequest).readyState);
if ((event.target as XMLHttpRequest).readyState === XMLHttpRequest.DONE) {
resolve();
}
});
xhr.open('GET', 'https://broken.to.cause.an.xhrnetworkerror.com.a.com');
xhr.send();
});
t.deepEqual(states, [
XMLHttpRequest.OPENED,
XMLHttpRequest.DONE
], 'right order of ready states');
});