6

这不是一个问题,而是一个有趣问题的结果。这也是一种“从失败中吸取教训”

我正在尝试为 IE 的 HTML5 历史鸭拳编写单元测试(使用 window.hash 作为状态维护的替代品)。鸭拳按预期工作,在用户测试期间,我在 IE、Chrome 和 Firefox 中获得了一致的结果。

问题出现的地方是单元测试。在它们中,我对 history.pushState()、.replaceState、.back() 和 .forward() 进行了各种组合。这些在 Firefox 和 IE 中运行良好,但 Chrome 给了我完全不一致的结果。下面的答案解释了原因。

4

2 回答 2

9

考虑以下:

var originalPath = window.location.pathname.toString();
history.pushState({ value: 'Foo' }, '', 'state-1');
history.pushState({ value: 'Bar' }, '', 'state-2');

history.pushState({ value: 'Baz' }, '', 'state-3');
history.back();
history.back();
console.log(history.state.value);
//So we can hit refresh and see the results again...
setTimeout(function () {
    history.replaceState(null, '', originalPath);
}, 250);

人们会期望这段代码返回“Foo”——在 Firefox 和我的 IE 鸭拳中,这正是它所做的——但在 Chrome 中,它以“Baz”响应。

经过一番调查,我解决了问题:IE和Firefox同步更新历史记录,如果需要加载任何页面,则异步进行。Chrome 似乎立即进入异步状态。

证据:

window.onpopstate = function () {
    console.log('ping');
}
history.pushState({ value: 'Foo' }, '', 'state-1');
history.back();
console.log('pong');

在 Firefox 中,这会返回 'ping';'pong' - 表示事件是作为 history.back() 调用的一部分分派的。在 Chrome 中,这会返回 'pong';'ping' - 表示事件被放置在队列中等待调度。

如果这个事件分派模型没有被用来管理历史和位置对象的状态,这不会那么糟糕 - 但显然它是。

window.onpopstate = function () {
    console.log('Event...', history.state, window.location.pathname.toString());
}
history.pushState({ value: 'Foo' }, '', 'state-1');
history.back();
console.log('Inline...', history.state, window.location.pathname.toString());

这是一个有趣的怪癖,需要使用 jQuery Deferred 链才能正常工作以进行我的单元测试。我对此并不特别高兴,但你能做什么?

于 2012-07-11T20:34:08.573 回答
2

为了在单元测试中处理异步返回事件,我使用了 HistoryJSJasmine。这是一个更新历史事件中的计数器以跟踪 chrome 何时处理事件和 Jasmine 的异步支持以阻止单元测试的情况,直到我们看到事件更改:

增加计数器:

History.Adapter.bind(window, 'statechange', function() {

    // Increment the counter
    window.historyEventCounter++;

});

Jasmine 异步单元测试。waitsFor 将阻塞,直到历史事件发生:

describe('back navigation', function () {
    it('should change url', function () {

        var initialHistoryEventCount;

        // Cache the initial count

        runs(function() {
            initialHistoryEventCount = window.historyEventCounter;
            History.back();
        });

        // Block until the event counter changes

        waitsFor(function() {
            return (initialHistoryEventCount !== app.historyEventCounter);
        }, "The page should be navigated back", 1000);

        // Now confirm the expected back behaviour. Event failures will time-out.

        runs(function() {
            expect(window.location.href).toEqual("http:// my back url");

            // ... more tests about the page state
        });
    }
}
于 2013-12-10T16:28:06.093 回答