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を使用しました。これは、履歴イベントのカウンターを更新して、クロムがいつイベントを処理したかを追跡し、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 に答える