request queue tests

This commit is contained in:
Carson Gross 2025-11-02 14:51:53 -07:00
parent c483c83667
commit d241e3052f
4 changed files with 216 additions and 7 deletions

View File

@ -12,10 +12,9 @@ var htmx = (() => {
} else {
// Update ctx.status properly for replaced request contexts
if (queueStrategy === "replace") {
this.#q.map(value => value.status = "dropped");
this.#q = []
if (this.#c) {
// TODO standardize on ctx.status
this.#c.cancelled = true;
this.#c.abort();
}
return true
@ -26,12 +25,15 @@ var htmx = (() => {
// ignore the request
ctx.status = "dropped";
} else if (queueStrategy === "queue last") {
this.#q.map(value => value.status = "dropped");
this.#q = [ctx]
ctx.status = "queued";
} else if (this.#q.length === 0) {
// default queue first
this.#q.push(ctx)
ctx.status = "queued";
} else {
ctx.status = "dropped";
}
return false
}
@ -487,7 +489,6 @@ var htmx = (() => {
raw: response,
status: response.status,
headers: response.headers,
cancelled: false,
}
if (!this.__trigger(elt, "htmx:after:request", {ctx})) return;
@ -499,9 +500,8 @@ var htmx = (() => {
} else {
// HTTP response
ctx.text = await response.text();
ctx.status = "response received";
if (!ctx.response.cancelled) {
if (ctx.status === "issuing") {
ctx.status = "response received";
this.__handleStatusCodes(ctx);
this.__handleHistoryUpdate(ctx);
await this.swap(ctx);

View File

@ -95,6 +95,7 @@
<script src="tests/unit/__collectFormData.js"></script>
<script src="tests/unit/__extractFilter.js"></script>
<script src="tests/unit/__findAllExt.js"></script>
<script src="tests/unit/__getRequestQueue.js"></script>
<script src="tests/unit/__handleHistoryUpdate.js"></script>
<script src="tests/unit/__handleStatusCodes.js"></script>
<script src="tests/unit/__handleTriggerEvent.js"></script>

View File

@ -0,0 +1,209 @@
describe('__getRequestQueue / RequestQueue unit tests', function() {
beforeEach(function() {
setupTest();
});
afterEach(function() {
cleanupTest();
});
it('allows first request when queue is empty', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let ctx = htmx.__createRequestContext(div, new Event('click'))
let queue = htmx.__getRequestQueue(div)
let result = queue.shouldIssueRequest(ctx, 'queue first')
assert.isTrue(result)
})
it('queues request with "queue all" strategy', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
// Issue first request
let ctx1 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx1, 'queue all')
// Queue second request
let ctx2 = htmx.__createRequestContext(div, new Event('click'))
let result = queue.shouldIssueRequest(ctx2, 'queue all')
assert.isFalse(result)
assert.equal(ctx2.status, 'queued')
})
it('drops request with "drop" strategy', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
// Issue first request
let ctx1 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx1, 'drop')
// Drop second request
let ctx2 = htmx.__createRequestContext(div, new Event('click'))
let result = queue.shouldIssueRequest(ctx2, 'drop')
assert.isFalse(result)
assert.equal(ctx2.status, 'dropped')
})
it('queues only last with "queue last" strategy', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
// Issue first request
let ctx1 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx1, 'queue last')
// Queue second request
let ctx2 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx2, 'queue last')
// Queue third request (should drop ctx2)
let ctx3 = htmx.__createRequestContext(div, new Event('click'))
let result = queue.shouldIssueRequest(ctx3, 'queue last')
assert.isFalse(result)
assert.equal(ctx2.status, 'dropped')
assert.equal(ctx3.status, 'queued')
})
it('replaces current request with "replace" strategy', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
// Issue first request
let ctx1 = htmx.__createRequestContext(div, new Event('click'))
ctx1.abort = () => { ctx1.aborted = true }
queue.shouldIssueRequest(ctx1, 'replace')
// Replace with second request
let ctx2 = htmx.__createRequestContext(div, new Event('click'))
let result = queue.shouldIssueRequest(ctx2, 'replace')
assert.isTrue(result)
assert.isTrue(ctx1.aborted)
})
it('defaults to "queue first" when strategy not specified', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
// Issue first request
let ctx1 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx1, 'queue first')
// Queue second request
let ctx2 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx2, 'queue first')
// Third request should be dropped (not queued)
let ctx3 = htmx.__createRequestContext(div, new Event('click'))
let result = queue.shouldIssueRequest(ctx3, 'queue first')
assert.isFalse(result)
assert.equal(ctx2.status, 'queued')
assert.equal(ctx3.status, 'dropped')
})
it('hasMore returns truthy when queue has requests', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
let ctx1 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx1, 'queue all')
let ctx2 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx2, 'queue all')
assert.isOk(queue.hasMore())
})
it('hasMore returns falsey when queue is empty', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
assert.isNotOk(queue.hasMore())
})
it('nextRequest returns next queued request', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
let ctx1 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx1, 'queue all')
let ctx2 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx2, 'queue all')
let next = queue.nextRequest()
assert.equal(next, ctx2)
})
it('nextRequest clears current request', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
let ctx1 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx1, 'queue all')
let ctx2 = htmx.__createRequestContext(div, new Event('click'))
queue.shouldIssueRequest(ctx2, 'queue all')
queue.nextRequest()
// Should now allow a new request
let ctx3 = htmx.__createRequestContext(div, new Event('click'))
let result = queue.shouldIssueRequest(ctx3, 'queue first')
assert.isTrue(result)
})
it('abortCurrentRequest calls abort on current request', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue = htmx.__getRequestQueue(div)
let ctx = htmx.__createRequestContext(div, new Event('click'))
ctx.abort = () => { ctx.aborted = true }
queue.shouldIssueRequest(ctx, 'queue first')
queue.abortCurrentRequest()
assert.isTrue(ctx.aborted)
})
it('returns same queue for same element', function () {
let div = createProcessedHTML('<div hx-get="/test"></div>')
let queue1 = htmx.__getRequestQueue(div)
let queue2 = htmx.__getRequestQueue(div)
assert.equal(queue1, queue2)
})
it('returns different queue for different elements', function () {
let div1 = createProcessedHTML('<div hx-get="/test1"></div>')
let div2 = createProcessedHTML('<div hx-get="/test2"></div>')
let queue1 = htmx.__getRequestQueue(div1)
let queue2 = htmx.__getRequestQueue(div2)
assert.notEqual(queue1, queue2)
})
it('uses selector from hx-sync for queue', function () {
let container = createProcessedHTML('<div id="container"><div id="btn1" hx-get="/test1" hx-sync="#container:drop"></div><div id="btn2" hx-get="/test2" hx-sync="#container:drop"></div></div>')
let btn1 = container.querySelector('#btn1')
let btn2 = container.querySelector('#btn2')
let queue1 = htmx.__getRequestQueue(btn1)
let queue2 = htmx.__getRequestQueue(btn2)
assert.equal(queue1, queue2)
})
});

View File

@ -135,7 +135,6 @@ describe('__issueRequest unit tests', function() {
assert.equal(ctx.response.status, 201)
assert.equal(ctx.response.headers, mockHeaders)
assert.equal(ctx.response.cancelled, false)
assert.isDefined(ctx.response.raw)
})