Skip to content

Commit

Permalink
Add FakeXMLHttpRequest
Browse files Browse the repository at this point in the history
  • Loading branch information
falsandtru committed Sep 5, 2023
1 parent 2686d55 commit 0f3b245
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 44 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 3.40.0

- Add FakeXMLHttpRequest.
- Change `fetch.rewrite` option.
- Change `update.rewrite` option.

## 3.39.1

- Fix noscript parsing.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@ Most SPA frameworks and pjax libraries lack many essential functions to keep the
|Execution sequence keeping| | || |
|Non-blocking script load|||| |
|**Subresource integrity verification**| | |✓<sup>\*1</sup>| |
|Lightweight source rewrite| ||| |
|**Rewrite request and response (XHR)**| | || |
|Rewrite source document| ||| |
|ETag support| | || |
|Cache|||||
|URL scope| | |||
|URL scope-based override settings| | |||
|**Browser history fix**| | || |
|**Scroll position restoration**| | || |
|**Unexpected scroll prevention**| | || |
|NOSCRIPT tag restoration| | || |
|NOSCRIPT tag fix| | || |
|History API support<sup>\*2</sup>| | || |
|No jQuery dependency| | |||

Expand Down
7 changes: 6 additions & 1 deletion gh-pages/_includes/nav.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
<li>
<a href="{{ site.basepath }}docs/apis/">APIs</a>
<ul class="nav nav-pills nav-stacked">
<li><a href="{{ site.basepath }}docs/apis/pjax/">Pjax</a></li>
<li><a href="{{ site.basepath }}docs/apis/pjax/">Pjax</a>
<ul class="nav nav-pills nav-stacked">
<li><a href="{{ site.basepath }}docs/apis/pjax/config/">Config</a></li>
</ul>
</li>
<li><a href="{{ site.basepath }}docs/apis/events/">Events</a></li>
<li><a href="{{ site.basepath }}docs/apis/util/">Util</a></li>
</ul>
</li>
</ul>
Expand Down
4 changes: 4 additions & 0 deletions gh-pages/docs/apis/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ Pjax APIs.
## [Events]({{ site.basepath }}docs/apis/events/)

Global events.

## [Util]({{ site.basepath }}docs/apis/util/)

Utilities.
4 changes: 2 additions & 2 deletions gh-pages/docs/apis/pjax/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Set a dictionary object having has/get/set/delete methods of Map to pass the doc

## fetch: {...} = ...

### rewrite: (path: string, method: string, headers: Headers, timeout: number, body: FormData | null) => XMLHttpRequest
### rewrite: (url: string, method: string, headers: Headers, timeout: number, body: FormData | null) => XMLHttpRequest | undefined

Rewrite the XHR object, or replace it with another or fake.

Expand All @@ -79,7 +79,7 @@ Wait for the specified milliseconds after sending a request.

## update: {...} = ...

### rewrite: (doc: Document, area: string, memory?: Document) => void = `() => undefined`
### rewrite: (url: string, document: Document, area: string, cache?: Document) => void = `() => undefined`

Rewrite the source document object.
If you use the sequence option, you should use only it instead of this.
Expand Down
31 changes: 31 additions & 0 deletions gh-pages/docs/apis/util/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
layout: layout
title: Util
type: page
nav: nav
class: style-api style-api-detail
---

# Util

## FakeXMLHttpRequest

Make a fake XHR object that doesn't send a request.

```ts
import Pjax, { FakeXMLHttpRequest } from 'pjax-api';

new Pjax({
fetch: {
rewrite: url =>
FakeXMLHttpRequest.create(
url,
fetch(url, { headers: { 'Content-Type': 'application/json' } })
.then(res => res.json())
.then(data =>
new DOMParser().parseFromString(
`<title>${data.title}</title><body>${data.body}</body>`,
'text/html'))),
},
});
```
2 changes: 2 additions & 0 deletions gh-pages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ This site is also powered by PJAX as a demo. Try page transitions.

- [APIs]({{ site.basepath }}docs/apis/)
- [Pjax]({{ site.basepath }}docs/apis/pjax/)
- [Config]({{ site.basepath }}docs/apis/pjax/config/)
- [Events]({{ site.basepath }}docs/apis/events/)
- [Util]({{ site.basepath }}docs/apis/util/)
</div>

<div class="col-md-4">
Expand Down
8 changes: 6 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ export interface Config {
readonly cache?: Dict<string, unknown>;
readonly memory?: Dict<string, Document>;
readonly fetch?: {
readonly rewrite?: (path: string, method: string, headers: Headers, timeout: number, body: FormData | null) => XMLHttpRequest;
readonly rewrite?: (url: string, method: string, headers: Headers, timeout: number, body: FormData | null) => XMLHttpRequest | undefined;
readonly headers?: Headers;
readonly timeout?: number;
readonly wait?: number;
};
readonly update?: {
readonly rewrite?: (path: string, doc: Document, area: string, memory: Document | undefined) => void;
readonly rewrite?: (url: string, document: Document, area: string, cache: Document | undefined) => void;
readonly head?: string;
readonly css?: boolean;
readonly script?: boolean;
Expand Down Expand Up @@ -63,3 +63,7 @@ declare global {
}

}

export class FakeXMLHttpRequest extends XMLHttpRequest {
public static create(url: string, response: Document | PromiseLike<Document>): FakeXMLHttpRequest;
}
1 change: 1 addition & 0 deletions src/export.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { GUI as Pjax, GUI as default } from './layer/interface/service/gui';
export { FakeXMLHttpRequest } from './lib/xhr';
2 changes: 1 addition & 1 deletion src/layer/domain/data/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class Config implements Option {
wait: 0,
};
public readonly update = {
rewrite: (_path: string, _doc: Document, _area: string, _memory: Document | undefined): void => undefined,
rewrite: (_url: string, _document: Document, _area: string, _cache: Document | undefined): void => undefined,
head: 'base, meta, link',
css: true,
script: true,
Expand Down
7 changes: 4 additions & 3 deletions src/layer/domain/router/module/fetch/xhr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export function xhr(
headers.set('If-None-Match', cache.get(displayURL.path)!.etag);
}
return new AtomicPromise<Either<Error, Response>>(resolve => {
const xhr = rewrite(displayURL.path, method, headers, timeout, body);
const xhr = rewrite(displayURL.href, method, headers, timeout, body) ??
request(displayURL.href, method, headers, timeout, body);

if (xhr.responseType !== 'document') throw new Error(`Response type must be 'document'`);

Expand Down Expand Up @@ -77,14 +78,14 @@ export function xhr(
}

function request(
path: URL.Path<StandardURL>,
url: URL.Href<StandardURL>,
method: RouterEventMethod,
headers: Headers,
timeout: number,
body: FormData | null,
): XMLHttpRequest {
const xhr = new XMLHttpRequest();
xhr.open(method, path, true);
xhr.open(method, url, true);
for (const [name, value] of headers) {
xhr.setRequestHeader(name, value);
}
Expand Down
2 changes: 1 addition & 1 deletion src/layer/domain/router/module/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export function update(
? config.memory?.get(event.location.dest.path)
: undefined;
config.update.rewrite(
event.location.dest.path,
event.location.dest.href,
documents.src,
area,
memory && separate({ src: memory, dst: documents.dst }, [area]).extract(() => false)
Expand Down
58 changes: 58 additions & 0 deletions src/lib/xhr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { AtomicPromise } from 'spica/promise';

export class FakeXMLHttpRequest extends XMLHttpRequest {
public static create(url: string, response: Document | PromiseLike<Document>): FakeXMLHttpRequest {
const xhr = new FakeXMLHttpRequest();
AtomicPromise.resolve(response)
.then(response => {
Object.defineProperties(xhr, {
responseURL: {
value: url,
},
responseXML: {
value: response,
},
});
xhr.send();
});
return xhr;
}
constructor() {
super();
this.responseType = 'document';
}
public override send(_?: Document | XMLHttpRequestBodyInit | null | undefined): void {
let state = 3;
Object.defineProperties(this, {
readyState: {
get: () => state,
},
status: {
value: 200,
},
statusText: {
value: 'OK',
},
response: {
get: () =>
this.responseType === 'document'
? this.responseXML
: this.responseText,
},
})
setTimeout(() => {
this.dispatchEvent(new ProgressEvent('loadstart'));
state = 4;
this.dispatchEvent(new ProgressEvent('loadend'));
this.dispatchEvent(new ProgressEvent('load'));
});
}
public override getResponseHeader(name: string): string | null {
switch (name.toLowerCase()) {
case 'content-type':
return 'text/html';
default:
return null;
}
}
}
34 changes: 10 additions & 24 deletions test/integration/config/fetch.rewrite.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Pjax } from '../../../index';
import { Pjax, FakeXMLHttpRequest } from '../../../index';
import { route as router } from '../../../src/layer/interface/service/router';
import { parse } from '../../../src/lib/html';
import { wait } from 'spica/timer';
import { once } from 'typed-dom';

describe('Integration: Config', function () {
Expand All @@ -10,33 +11,18 @@ describe('Integration: Config', function () {

describe('fetch.rewrite', function () {
it('', function (done) {
const FakeXMLHttpRequest = XMLHttpRequest;
const url = '/base/test/integration/fixture/basic/1.html';
const document = parse('').extract();
new Pjax({
fetch: {
rewrite: (path, method, headers, timeout, body) => {
const xhr = new FakeXMLHttpRequest();
xhr.open(method, path, true);
for (const [name, value] of headers) {
xhr.setRequestHeader(name, value);
}

xhr.responseType = 'document';
xhr.timeout = timeout;
xhr.send(body);

Object.defineProperties(xhr, {
responseURL: {
value: url,
},
responseXML: {
value: parse('<title>Title 2</title><div id="primary">Primary 2</div>').extract(),
},
});
return xhr;
},
}
rewrite: url =>
FakeXMLHttpRequest.create(
url,
wait(100).then(() =>
new DOMParser().parseFromString(
'<title>Title 2</title><div id="primary">Primary 2</div>',
'text/html'))),
},
}, { document, router })
.assign(url);
once(document, 'pjax:ready', () => {
Expand Down
13 changes: 7 additions & 6 deletions test/integration/config/update.rewrite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ describe('Integration: Config', function () {
new Pjax({
memory: new Cache(100),
update: {
rewrite(path, doc, area, memory) {
switch (path) {
case url:
memory && doc.querySelector(area)?.replaceWith(memory.querySelector(area)!.cloneNode(true));
rewrite(url, doc, area, cache) {
switch (url.split('/').at(-1)) {
case '2.html':
cache && doc.querySelector(area)?.replaceWith(cache.querySelector(area)!.cloneNode(true));
}
},
}
},
}, { document, router })
.assign(url);
once(document, 'pjax:ready', () => {
Expand All @@ -32,7 +32,8 @@ describe('Integration: Config', function () {
document.querySelector('#primary')!.textContent = 'PRIMARY 2';
once(document, 'pjax:ready', () => {
assert(window.location.pathname !== url);
assert(document.title !== 'Title 2');
assert(document.title === 'Title 1');
assert(document.querySelector('#primary')!.textContent === 'Primary 1');
once(document, 'pjax:ready', () => {
assert(window.location.pathname === url);
assert(document.title === 'Title 2');
Expand Down
8 changes: 6 additions & 2 deletions test/interface/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import _Pjax, { Pjax } from '../../index';
import Pjax$, { Pjax, FakeXMLHttpRequest } from '../../index';

describe('Interface: Package', function () {
describe('default', function () {
it('default', function () {
assert(_Pjax === Pjax);
assert(Pjax$ === Pjax);
});

});
Expand All @@ -19,4 +19,8 @@ describe('Interface: Package', function () {

});

describe('FakeXMLHttpRequest', function () {
assert(typeof FakeXMLHttpRequest === 'function');
});

});

0 comments on commit 0f3b245

Please sign in to comment.