|
|
|
@@ -14,7 +14,7 @@ def run_worker_scenario(scenario: str) -> dict:
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
const vm = require('vm');
|
|
|
|
|
const listeners = {{}};
|
|
|
|
|
const state = {{ added: [], individuallyAdded: [], failedAdds: [], deleted: [], deletedDatabases: [], claimed: false, skipped: false, fetches: [], puts: [], sharedRecords: {{}}, failSharedPut: false, backgroundFlushes: 0, backgroundResumes: 0, outboxPurges: 0, outboxLifecycle: [], notifications: [], focused: [], opened: [], failFetch: false, stallFetch: false, lateFetch: false, fetchAborted: false, fetchStatus: 200, fetchRedirected: false, cachedBody: null }};
|
|
|
|
|
const state = {{ added: [], addAttempts: [], individuallyAdded: [], failedAdds: [], deleted: [], deletedDatabases: [], claimed: false, skipped: false, fetches: [], puts: [], migrated: [], activationOrder: [], oldCachedAssets: {{}}, sharedRecords: {{}}, failSharedPut: false, backgroundFlushes: 0, backgroundResumes: 0, outboxPurges: 0, outboxLifecycle: [], notifications: [], focused: [], opened: [], failFetch: false, stallFetch: false, lateFetch: false, fetchAborted: false, fetchStatus: 200, fetchRedirected: false, cachedBody: null }};
|
|
|
|
|
const storedResponses = new Map();
|
|
|
|
|
storedResponses.set(
|
|
|
|
|
'https://forge.example/dashboard/__offline-session-lease',
|
|
|
|
@@ -26,6 +26,7 @@ storedResponses.set(
|
|
|
|
|
const cache = {{
|
|
|
|
|
addAll: async urls => {{ state.added = urls; }},
|
|
|
|
|
add: async url => {{
|
|
|
|
|
state.addAttempts.push(url);
|
|
|
|
|
if (state.failedAdds.includes(url)) throw new Error('optional asset unavailable');
|
|
|
|
|
state.individuallyAdded.push(url);
|
|
|
|
|
}},
|
|
|
|
@@ -37,9 +38,18 @@ const cache = {{
|
|
|
|
|
put: async (request, response) => {{
|
|
|
|
|
const key = String(request.url || request);
|
|
|
|
|
state.puts.push(key);
|
|
|
|
|
state.migrated.push(key);
|
|
|
|
|
state.activationOrder.push('put:' + key);
|
|
|
|
|
storedResponses.set(key, response.clone());
|
|
|
|
|
}},
|
|
|
|
|
}};
|
|
|
|
|
const oldCache = {{
|
|
|
|
|
match: async request => {{
|
|
|
|
|
const key = String(request.url || request);
|
|
|
|
|
const body = state.oldCachedAssets[key];
|
|
|
|
|
return body === undefined ? null : new Response(body);
|
|
|
|
|
}},
|
|
|
|
|
}};
|
|
|
|
|
const context = {{
|
|
|
|
|
URL, Request, Response, Headers, AbortController,
|
|
|
|
|
setTimeout, clearTimeout, console,
|
|
|
|
@@ -79,9 +89,9 @@ const context = {{
|
|
|
|
|
return request;
|
|
|
|
|
}}}},
|
|
|
|
|
caches: {{
|
|
|
|
|
open: async () => cache,
|
|
|
|
|
open: async key => key === 'stackchain-dashboard-old' ? oldCache : cache,
|
|
|
|
|
keys: async () => ['stackchain-dashboard-old', 'another-app-cache'],
|
|
|
|
|
delete: async key => {{ state.deleted.push(key); return true; }},
|
|
|
|
|
delete: async key => {{ state.deleted.push(key); state.activationOrder.push('delete:' + key); return true; }},
|
|
|
|
|
match: async request => cache.match(request),
|
|
|
|
|
}},
|
|
|
|
|
fetch: async (request, options = {{}}) => {{
|
|
|
|
@@ -152,10 +162,16 @@ async function dispatchPush(payload) {{
|
|
|
|
|
return json.loads(completed.stdout)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_offline_activation_migration_rolls_the_shell_cache():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_resumable_today_session_ships_in_a_new_offline_shell():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/my-work.js'" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.js'" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.css'" in source
|
|
|
|
@@ -164,7 +180,7 @@ def test_resumable_today_session_ships_in_a_new_offline_shell():
|
|
|
|
|
def test_mobile_conversation_photo_bundles_roll_the_offline_shell():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.js'" in source
|
|
|
|
|
assert "BASE + 'static/authored-outbox.js'" in source
|
|
|
|
|
assert "BASE + 'static/background-issue-sync.js'" in source
|
|
|
|
@@ -173,7 +189,7 @@ def test_mobile_conversation_photo_bundles_roll_the_offline_shell():
|
|
|
|
|
def test_photo_metadata_sanitizer_rolls_the_cached_optimizer_atomically():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/issue-evidence-review.js'" in source
|
|
|
|
|
assert "BASE + 'static/issue-attachment.js'" in source
|
|
|
|
|
|
|
|
|
@@ -181,14 +197,14 @@ def test_photo_metadata_sanitizer_rolls_the_cached_optimizer_atomically():
|
|
|
|
|
def test_ownership_exit_runtime_rolls_the_offline_shell_cache():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.js'" in source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_offline_review_next_ships_today_completion_atomically():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/today-completion.js'" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.js'" in source
|
|
|
|
|
|
|
|
|
@@ -196,7 +212,7 @@ def test_offline_review_next_ships_today_completion_atomically():
|
|
|
|
|
def test_duplicate_aware_capture_ships_in_a_new_offline_shell():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/create-issue-sheet.js'" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.js'" in source
|
|
|
|
|
|
|
|
|
@@ -204,7 +220,7 @@ def test_duplicate_aware_capture_ships_in_a_new_offline_shell():
|
|
|
|
|
def test_inline_checklist_step_flow_rolls_the_offline_shell_atomically():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/issue-sheet.js'" in source
|
|
|
|
|
assert "BASE + 'static/checklist-conflict.js'" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.js'" in source
|
|
|
|
@@ -214,14 +230,14 @@ def test_inline_checklist_step_flow_rolls_the_offline_shell_atomically():
|
|
|
|
|
def test_exact_later_picker_ships_atomically_in_a_new_offline_shell():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/later-picker.js'" in source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_navigation_deadline_ships_in_a_new_shell_cache():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.css'" in source
|
|
|
|
|
assert "BASE + 'static/dashboard.js'" in source
|
|
|
|
|
assert "BASE + 'static/install-app.js'" in source
|
|
|
|
@@ -230,21 +246,21 @@ def test_navigation_deadline_ships_in_a_new_shell_cache():
|
|
|
|
|
def test_today_convergence_ships_in_a_new_shell_cache():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/today-sync.js'" in source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_mobile_search_viewport_ships_in_a_new_offline_shell():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/mobile-search-viewport.js'" in source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_update_ownership_flow_ships_atomically_in_a_new_offline_shell():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/update-ownership.js'" in source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -914,7 +930,7 @@ def test_one_session_bound_csrf_proof_is_reused_for_a_background_drain():
|
|
|
|
|
def test_queue_today_ships_atomically_in_a_new_offline_shell():
|
|
|
|
|
source = WORKER.read_text()
|
|
|
|
|
|
|
|
|
|
assert "stackchain-dashboard-shell-v113" in source
|
|
|
|
|
assert "stackchain-dashboard-shell-v114" in source
|
|
|
|
|
assert "BASE + 'static/queue-today.js'" in source
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1056,6 +1072,30 @@ def test_activate_deletes_only_stale_stackchain_caches():
|
|
|
|
|
assert result["deleted"] == ["stackchain-dashboard-old"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_offline_activation_migrates_cached_optional_feature_before_deleting_old_cache():
|
|
|
|
|
result = run_worker_scenario(
|
|
|
|
|
"""
|
|
|
|
|
const asset = '/dashboard/feature-issue-capture-test.js';
|
|
|
|
|
state.oldCachedAssets[asset] = 'previous cached feature';
|
|
|
|
|
state.failedAdds = [asset];
|
|
|
|
|
context.self.__testOptionalFeatures.push(asset);
|
|
|
|
|
await dispatch('activate');
|
|
|
|
|
process.stdout.write(JSON.stringify(state));
|
|
|
|
|
"""
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert result["migrated"] == [
|
|
|
|
|
"/dashboard/feature-issue-capture-test.js"
|
|
|
|
|
]
|
|
|
|
|
assert result["individuallyAdded"] == []
|
|
|
|
|
assert "/dashboard/feature-issue-capture-test.js" not in result["addAttempts"]
|
|
|
|
|
assert result["activationOrder"] == [
|
|
|
|
|
"put:/dashboard/feature-issue-capture-test.js",
|
|
|
|
|
"delete:stackchain-dashboard-old",
|
|
|
|
|
]
|
|
|
|
|
assert result["claimed"] is True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_activate_warms_optional_features_without_blocking_siblings_or_claim():
|
|
|
|
|
result = run_worker_scenario(
|
|
|
|
|
"""
|
|
|
|
|