Compare commits
No commits in common. "f92794f571e380534a3f2cccf5c01c9147a5e319" and "cbdddcddbe0630507e1ed4db63417ffb99a91eb1" have entirely different histories.
f92794f571
...
cbdddcddbe
|
|
@ -1,36 +0,0 @@
|
||||||
function createCreateAndStart({ todayWork, todaySync, refresh, warm, start, announce }) {
|
|
||||||
const completed = new Set();
|
|
||||||
|
|
||||||
function available() {
|
|
||||||
return todayWork.read().length < todayWork.limit;
|
|
||||||
}
|
|
||||||
|
|
||||||
function complete(issue) {
|
|
||||||
const identity = todayWork.identity(issue);
|
|
||||||
if (!identity || completed.has(identity)) return 'exists';
|
|
||||||
const added = todayWork.add(issue);
|
|
||||||
if (added === 'full') {
|
|
||||||
announce('Created, but Today changed—remove an item, then add this issue.');
|
|
||||||
return 'full';
|
|
||||||
}
|
|
||||||
if (added !== 'added' && added !== 'exists') {
|
|
||||||
announce('Created, but Today could not be saved on this device.');
|
|
||||||
return 'unavailable';
|
|
||||||
}
|
|
||||||
completed.add(identity);
|
|
||||||
if (added === 'added' && !todaySync.enqueue('add', identity)) {
|
|
||||||
announce('Created and saved to Today on this device, but account sync is unavailable.');
|
|
||||||
return 'sync-unavailable';
|
|
||||||
}
|
|
||||||
refresh();
|
|
||||||
todaySync.flush();
|
|
||||||
warm();
|
|
||||||
start(issue);
|
|
||||||
announce('Created, added to Today, and ready to work.');
|
|
||||||
return 'started';
|
|
||||||
}
|
|
||||||
|
|
||||||
return { available, complete };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof module !== 'undefined' && module.exports) module.exports = createCreateAndStart;
|
|
||||||
|
|
@ -110,9 +110,7 @@ textarea { resize: vertical; min-height: 120px; }
|
||||||
.draft-preview { color:var(--muted); overflow-wrap:anywhere; }
|
.draft-preview { color:var(--muted); overflow-wrap:anywhere; }
|
||||||
.draft-actions { display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:8px; }
|
.draft-actions { display:grid; grid-template-columns:repeat(auto-fit,minmax(120px,1fr)); gap:8px; }
|
||||||
.draft-actions button { min-height:44px; width:100%; }
|
.draft-actions button { min-height:44px; width:100%; }
|
||||||
.create-issue-actions { display:grid; grid-template-columns:repeat(auto-fit,minmax(140px,1fr)); gap:8px; padding-bottom:env(safe-area-inset-bottom); }
|
.create-issue-actions button { min-height:44px; max-width:100%; }
|
||||||
.create-issue-actions button { min-height:44px; max-width:100%; width:100%; }
|
|
||||||
.create-issue-actions #create-issue-status { grid-column:1/-1; }
|
|
||||||
.my-work-card { min-height: 44px; display:grid; gap:8px; padding:12px; border:1px solid #1f3a5f; border-radius:12px; background:#0f1d33; color:var(--text); }
|
.my-work-card { min-height: 44px; display:grid; gap:8px; padding:12px; border:1px solid #1f3a5f; border-radius:12px; background:#0f1d33; color:var(--text); }
|
||||||
.my-work-card-main { display:block; width:100%; color:var(--text); text-align:left; font:inherit; background:transparent; border:0; padding:0; }
|
.my-work-card-main { display:block; width:100%; color:var(--text); text-align:left; font:inherit; background:transparent; border:0; padding:0; }
|
||||||
.my-work-card-main.review-trigger { width:100%; text-align:left; font:inherit; }
|
.my-work-card-main.review-trigger { width:100%; text-align:left; font:inherit; }
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,6 @@
|
||||||
let pullConversation = null;
|
let pullConversation = null;
|
||||||
let pullReviewState = null;
|
let pullReviewState = null;
|
||||||
let creatingIssue = false;
|
let creatingIssue = false;
|
||||||
let createAndStartRequested = false;
|
|
||||||
let findingWork = false;
|
let findingWork = false;
|
||||||
let availablePagination = { page: 1, total: 0, has_more: false };
|
let availablePagination = { page: 1, total: 0, has_more: false };
|
||||||
let progress = null;
|
let progress = null;
|
||||||
|
|
@ -689,18 +688,6 @@
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const createAndStart = createCreateAndStart({
|
|
||||||
todayWork,
|
|
||||||
todaySync,
|
|
||||||
refresh: refreshMyWorkView,
|
|
||||||
warm: warmTodayOffline,
|
|
||||||
start: item => {
|
|
||||||
qs('[data-work-filter="today"]').click();
|
|
||||||
workSession.start(item);
|
|
||||||
},
|
|
||||||
announce: message => { qs('#my-work-action-status').textContent = message; },
|
|
||||||
});
|
|
||||||
|
|
||||||
let planTodayTrigger = null;
|
let planTodayTrigger = null;
|
||||||
function planTodayItemMarkup(item, selected, index = -1) {
|
function planTodayItemMarkup(item, selected, index = -1) {
|
||||||
const id = todayWork.identity(item);
|
const id = todayWork.identity(item);
|
||||||
|
|
@ -1961,7 +1948,6 @@
|
||||||
scheduleIssueDuplicateCheck();
|
scheduleIssueDuplicateCheck();
|
||||||
qs('#create-issue-status').textContent = repositories.length ? '' : 'No accessible repositories are available.';
|
qs('#create-issue-status').textContent = repositories.length ? '' : 'No accessible repositories are available.';
|
||||||
qs('#submit-new-issue').disabled = !repositories.length;
|
qs('#submit-new-issue').disabled = !repositories.length;
|
||||||
qs('#create-and-start-issue').disabled = !repositories.length || !createAndStart.available();
|
|
||||||
qs('#create-issue-sheet').classList.add('open');
|
qs('#create-issue-sheet').classList.add('open');
|
||||||
creatingIssue = true;
|
creatingIssue = true;
|
||||||
qs('#create-issue-title').focus();
|
qs('#create-issue-title').focus();
|
||||||
|
|
@ -1981,7 +1967,7 @@
|
||||||
qs('#new-issue').focus();
|
qs('#new-issue').focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyOutboxResult(result, openCreated = false, startCreated = false) {
|
function applyOutboxResult(result, openCreated = false) {
|
||||||
if (result.lease_skipped) { refreshMyWorkView(); return; }
|
if (result.lease_skipped) { refreshMyWorkView(); return; }
|
||||||
(result.confirmed || []).forEach(confirmed => {
|
(result.confirmed || []).forEach(confirmed => {
|
||||||
if (lastContextSnapshot) lastContextSnapshot.issues = [confirmed].concat(lastContextSnapshot.issues || []);
|
if (lastContextSnapshot) lastContextSnapshot.issues = [confirmed].concat(lastContextSnapshot.issues || []);
|
||||||
|
|
@ -1995,10 +1981,7 @@
|
||||||
const created = lastMyWork.find(item =>
|
const created = lastMyWork.find(item =>
|
||||||
item.kind === 'issue' && item.repository === confirmed.repository && item.number === confirmed.number
|
item.kind === 'issue' && item.repository === confirmed.repository && item.number === confirmed.number
|
||||||
);
|
);
|
||||||
if (startCreated && created) {
|
if (openCreated && created) openRoutedWork(created, qs('#new-issue'));
|
||||||
const outcome = createAndStart.complete(created);
|
|
||||||
if (outcome !== 'started' && openCreated) openRoutedWork(created, qs('#new-issue'));
|
|
||||||
} else if (openCreated && created) openRoutedWork(created, qs('#new-issue'));
|
|
||||||
} else if ((result.remaining || []).some(item => item.status === 'attention')) {
|
} else if ((result.remaining || []).some(item => item.status === 'attention')) {
|
||||||
qs('#my-work-action-status').textContent = 'Needs attention · edit the queued issue before sending again.';
|
qs('#my-work-action-status').textContent = 'Needs attention · edit the queued issue before sending again.';
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2749,7 +2732,6 @@
|
||||||
});
|
});
|
||||||
qs('#create-issue-form').addEventListener('submit', async event => {
|
qs('#create-issue-form').addEventListener('submit', async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (event.submitter) createAndStartRequested = event.submitter?.id === 'create-and-start-issue';
|
|
||||||
const captureDraft = currentIssueCaptureDraft();
|
const captureDraft = currentIssueCaptureDraft();
|
||||||
if (!captureDraft.repository || !captureDraft.title) {
|
if (!captureDraft.repository || !captureDraft.title) {
|
||||||
qs('#create-issue-status').textContent = 'Choose a repository and add a title.';
|
qs('#create-issue-status').textContent = 'Choose a repository and add a title.';
|
||||||
|
|
@ -2762,17 +2744,9 @@
|
||||||
qs('#create-issue-anyway').focus();
|
qs('#create-issue-anyway').focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (createAndStartRequested && !createAndStart.available()) {
|
|
||||||
qs('#create-issue-status').textContent = 'Today is full. Remove an item before creating and starting another.';
|
|
||||||
qs('#create-and-start-issue').focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const button = qs('#submit-new-issue');
|
const button = qs('#submit-new-issue');
|
||||||
const startButton = qs('#create-and-start-issue');
|
|
||||||
button.disabled = true;
|
button.disabled = true;
|
||||||
startButton.disabled = true;
|
qs('#create-issue-status').textContent = 'Saving for background delivery…';
|
||||||
qs('#create-issue-status').textContent = createAndStartRequested ?
|
|
||||||
'Creating issue and adding it to Today…' : 'Saving for background delivery…';
|
|
||||||
try {
|
try {
|
||||||
const admission = editingOutboxId ? await issueOutbox.updateDurably(editingOutboxId, captureDraft) :
|
const admission = editingOutboxId ? await issueOutbox.updateDurably(editingOutboxId, captureDraft) :
|
||||||
await issueOutbox.enqueueDurably(captureDraft);
|
await issueOutbox.enqueueDurably(captureDraft);
|
||||||
|
|
@ -2782,7 +2756,6 @@
|
||||||
refreshMyWorkView();
|
refreshMyWorkView();
|
||||||
qs('#create-issue-status').textContent = 'Saved for next launch; background delivery unavailable.';
|
qs('#create-issue-status').textContent = 'Saved for next launch; background delivery unavailable.';
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
startButton.disabled = !createAndStart.available();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
editingOutboxId = null;
|
editingOutboxId = null;
|
||||||
|
|
@ -2791,14 +2764,10 @@
|
||||||
taskOverlayHistory.leave();
|
taskOverlayHistory.leave();
|
||||||
refreshMyWorkView();
|
refreshMyWorkView();
|
||||||
qs('#my-work-action-status').textContent = 'Queued for sync.';
|
qs('#my-work-action-status').textContent = 'Queued for sync.';
|
||||||
if (navigator.onLine) applyOutboxResult(
|
if (navigator.onLine) applyOutboxResult(await issueOutbox.retry(queued.id, activeFlushLogin), true);
|
||||||
await issueOutbox.retry(queued.id, activeFlushLogin), true, createAndStartRequested
|
|
||||||
);
|
|
||||||
createAndStartRequested = false;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
qs('#create-issue-status').textContent = error.message + ' Your draft is safe; retry.';
|
qs('#create-issue-status').textContent = error.message + ' Your draft is safe; retry.';
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
startButton.disabled = !createAndStart.available();
|
|
||||||
qs('#create-issue-title').focus();
|
qs('#create-issue-title').focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -389,7 +389,6 @@
|
||||||
<div class="create-issue-actions">
|
<div class="create-issue-actions">
|
||||||
<button id="save-unfiled-issue" type="button">Save for filing</button>
|
<button id="save-unfiled-issue" type="button">Save for filing</button>
|
||||||
<button id="submit-new-issue" type="submit">Create & assign to me</button>
|
<button id="submit-new-issue" type="submit">Create & assign to me</button>
|
||||||
<button id="create-and-start-issue" type="submit">Create & start</button>
|
|
||||||
<div id="create-issue-status" class="small" aria-live="assertive"></div>
|
<div id="create-issue-status" class="small" aria-live="assertive"></div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
@ -594,7 +593,6 @@
|
||||||
<script src="static/conversation.js"></script>
|
<script src="static/conversation.js"></script>
|
||||||
<script src="static/issue-sheet.js"></script>
|
<script src="static/issue-sheet.js"></script>
|
||||||
<script src="static/create-issue-sheet.js"></script>
|
<script src="static/create-issue-sheet.js"></script>
|
||||||
<script src="static/create-and-start.js"></script>
|
|
||||||
<script src="static/pull-sheet.js"></script>
|
<script src="static/pull-sheet.js"></script>
|
||||||
<script src="static/review-sheet.js"></script>
|
<script src="static/review-sheet.js"></script>
|
||||||
<script src="static/work-route.js"></script>
|
<script src="static/work-route.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -502,12 +502,11 @@ function createWorkSession({ getItems, getFilter, getMilestone = () => 'all', on
|
||||||
|
|
||||||
return {
|
return {
|
||||||
active: () => running,
|
active: () => running,
|
||||||
start(item = null) {
|
start() {
|
||||||
const items = queue();
|
const items = queue();
|
||||||
if (!items.length) return finish();
|
if (!items.length) return finish();
|
||||||
running = true;
|
running = true;
|
||||||
const requested = item ? items.findIndex(candidate => workIdentity(candidate) === workIdentity(item)) : 0;
|
return openAt(items, 0);
|
||||||
return openAt(items, requested >= 0 ? requested : 0);
|
|
||||||
},
|
},
|
||||||
reconcile() {
|
reconcile() {
|
||||||
if (!running) return false;
|
if (!running) return false;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
const BASE = new URL('./', self.location.href).pathname;
|
const BASE = new URL('./', self.location.href).pathname;
|
||||||
importScripts(BASE + 'static/background-issue-sync.js');
|
importScripts(BASE + 'static/background-issue-sync.js');
|
||||||
const CACHE = 'stackchain-dashboard-shell-v58';
|
const CACHE = 'stackchain-dashboard-shell-v57';
|
||||||
const OUTAGE_STATUSES = new Set([500, 502, 503, 504]);
|
const OUTAGE_STATUSES = new Set([500, 502, 503, 504]);
|
||||||
const NAVIGATION_TIMEOUT_MS = self.__STACKCHAIN_NAVIGATION_TIMEOUT_MS || 4000;
|
const NAVIGATION_TIMEOUT_MS = self.__STACKCHAIN_NAVIGATION_TIMEOUT_MS || 4000;
|
||||||
const SHELL = [
|
const SHELL = [
|
||||||
|
|
@ -36,7 +36,6 @@ const SHELL = [
|
||||||
BASE + 'static/conversation.js',
|
BASE + 'static/conversation.js',
|
||||||
BASE + 'static/issue-sheet.js',
|
BASE + 'static/issue-sheet.js',
|
||||||
BASE + 'static/create-issue-sheet.js',
|
BASE + 'static/create-issue-sheet.js',
|
||||||
BASE + 'static/create-and-start.js',
|
|
||||||
BASE + 'static/pull-sheet.js',
|
BASE + 'static/pull-sheet.js',
|
||||||
BASE + 'static/review-sheet.js',
|
BASE + 'static/review-sheet.js',
|
||||||
BASE + 'static/work-route.js',
|
BASE + 'static/work-route.js',
|
||||||
|
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
CREATE_AND_START = Path(__file__).parents[1] / "frontend" / "create-and-start.js"
|
|
||||||
HTML = Path(__file__).parents[1] / "frontend" / "index.html"
|
|
||||||
DASHBOARD = Path(__file__).parents[1] / "frontend" / "dashboard.js"
|
|
||||||
CSS = Path(__file__).parents[1] / "frontend" / "dashboard.css"
|
|
||||||
WORKER = Path(__file__).parents[1] / "frontend" / "service-worker.js"
|
|
||||||
|
|
||||||
|
|
||||||
def run_node(script):
|
|
||||||
return json.loads(subprocess.run(
|
|
||||||
["node", "-e", script], check=True, capture_output=True, text=True
|
|
||||||
).stdout)
|
|
||||||
|
|
||||||
|
|
||||||
def test_confirmed_issue_is_planned_synced_warmed_and_started_once():
|
|
||||||
script = f"""
|
|
||||||
const fs = require('fs');
|
|
||||||
if (!fs.existsSync({json.dumps(str(CREATE_AND_START))})) {{
|
|
||||||
process.stdout.write(JSON.stringify({{available:false}}));
|
|
||||||
}} else {{
|
|
||||||
const createCreateAndStart = require({json.dumps(str(CREATE_AND_START))});
|
|
||||||
const calls = [];
|
|
||||||
const issue = {{kind:'issue', repository:'stackchain/dashboard', number:387}};
|
|
||||||
const flow = createCreateAndStart({{
|
|
||||||
todayWork: {{limit:5, read:()=>[], identity:item=>'issue:'+item.repository+':'+item.number+':', add:()=>{{calls.push('add');return 'added';}}}},
|
|
||||||
todaySync: {{enqueue:(action,id)=>{{calls.push(action+':'+id);return true;}}, flush:()=>calls.push('flush')}},
|
|
||||||
refresh:()=>calls.push('refresh'), warm:()=>calls.push('warm'), start:item=>calls.push('start:'+item.number),
|
|
||||||
announce:message=>calls.push('announce:'+message),
|
|
||||||
}});
|
|
||||||
const result = flow.complete(issue);
|
|
||||||
flow.complete(issue);
|
|
||||||
process.stdout.write(JSON.stringify({{available:true,result,calls}}));
|
|
||||||
}}
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert run_node(script) == {
|
|
||||||
"available": True,
|
|
||||||
"result": "started",
|
|
||||||
"calls": [
|
|
||||||
"add",
|
|
||||||
"add:issue:stackchain/dashboard:387:",
|
|
||||||
"refresh",
|
|
||||||
"flush",
|
|
||||||
"warm",
|
|
||||||
"start:387",
|
|
||||||
"announce:Created, added to Today, and ready to work.",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_mobile_capture_wires_distinct_create_and_start_intent_into_the_offline_shell():
|
|
||||||
html = HTML.read_text()
|
|
||||||
dashboard = DASHBOARD.read_text()
|
|
||||||
css = CSS.read_text()
|
|
||||||
worker = WORKER.read_text()
|
|
||||||
|
|
||||||
assert '<button id="create-and-start-issue" type="submit">Create & start</button>' in html
|
|
||||||
assert '<script src="static/create-and-start.js"></script>' in html
|
|
||||||
assert "const createAndStart = createCreateAndStart({" in dashboard
|
|
||||||
assert "event.submitter?.id === 'create-and-start-issue'" in dashboard
|
|
||||||
assert "createAndStart.complete(created)" in dashboard
|
|
||||||
assert ".create-issue-actions" in css and "grid-template-columns" in css
|
|
||||||
assert "BASE + 'static/create-and-start.js'" in worker
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_and_start_stops_before_session_when_today_sync_cannot_be_queued():
|
|
||||||
script = f"""
|
|
||||||
const createCreateAndStart = require({json.dumps(str(CREATE_AND_START))});
|
|
||||||
const calls = [];
|
|
||||||
const issue = {{kind:'issue', repository:'stackchain/dashboard', number:388}};
|
|
||||||
const flow = createCreateAndStart({{
|
|
||||||
todayWork: {{limit:5, read:()=>[], identity:()=> 'issue:stackchain/dashboard:388:', add:()=> 'added'}},
|
|
||||||
todaySync: {{enqueue:()=>false, flush:()=>calls.push('flush')}},
|
|
||||||
refresh:()=>calls.push('refresh'), warm:()=>calls.push('warm'), start:()=>calls.push('start'),
|
|
||||||
announce:message=>calls.push(message),
|
|
||||||
}});
|
|
||||||
process.stdout.write(JSON.stringify({{result:flow.complete(issue), calls}}));
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert run_node(script) == {
|
|
||||||
"result": "sync-unavailable",
|
|
||||||
"calls": ["Created and saved to Today on this device, but account sync is unavailable."],
|
|
||||||
}
|
|
||||||
|
|
@ -347,5 +347,5 @@ async def test_dashboard_syncs_every_later_change_and_exposes_account_status():
|
||||||
def test_later_sync_ships_atomically_in_the_offline_shell():
|
def test_later_sync_ships_atomically_in_the_offline_shell():
|
||||||
source = (Path(__file__).parents[1] / "frontend" / "service-worker.js").read_text()
|
source = (Path(__file__).parents[1] / "frontend" / "service-worker.js").read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/later-sync.js'" in source
|
assert "BASE + 'static/later-sync.js'" in source
|
||||||
|
|
|
||||||
|
|
@ -137,4 +137,4 @@ def test_markdown_work_bodies_are_mobile_safe_block_containers():
|
||||||
assert ".markdown-content { min-width:0; max-width:100%; overflow-wrap:anywhere;" in css
|
assert ".markdown-content { min-width:0; max-width:100%; overflow-wrap:anywhere;" in css
|
||||||
assert ".markdown-content pre { max-width:100%; overflow-x:auto;" in css
|
assert ".markdown-content pre { max-width:100%; overflow-x:auto;" in css
|
||||||
assert ".markdown-content a { min-height:44px;" in css
|
assert ".markdown-content a { min-height:44px;" in css
|
||||||
assert "stackchain-dashboard-shell-v58" in worker
|
assert "stackchain-dashboard-shell-v57" in worker
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,4 @@ def test_offline_shell_contains_every_local_dashboard_runtime_asset():
|
||||||
shell_assets = set(re.findall(r"BASE \+ '([^']+)'", worker.split("async function sessionCsrf", 1)[0]))
|
shell_assets = set(re.findall(r"BASE \+ '([^']+)'", worker.split("async function sessionCsrf", 1)[0]))
|
||||||
|
|
||||||
assert local_assets <= shell_assets, f"Offline shell is missing: {sorted(local_assets - shell_assets)}"
|
assert local_assets <= shell_assets, f"Offline shell is missing: {sorted(local_assets - shell_assets)}"
|
||||||
assert "stackchain-dashboard-shell-v58" in worker
|
assert "stackchain-dashboard-shell-v57" in worker
|
||||||
|
|
|
||||||
|
|
@ -1376,29 +1376,6 @@ async def test_dashboard_wires_work_session_to_existing_sheet_flows_and_completi
|
||||||
assert opener in html
|
assert opener in html
|
||||||
|
|
||||||
|
|
||||||
def test_work_session_can_start_at_a_newly_created_item():
|
|
||||||
script = f"""
|
|
||||||
const buildMyWork = require({json.dumps(str(MY_WORK))});
|
|
||||||
const items = [1, 2, 3].map(number => ({{kind:'issue', repository:'stackchain/dashboard', number}}));
|
|
||||||
const opened = [];
|
|
||||||
const session = buildMyWork.createWorkSession({{
|
|
||||||
getItems: () => items,
|
|
||||||
getFilter: () => 'all',
|
|
||||||
getMilestone: () => '',
|
|
||||||
onOpen: item => opened.push(item.number),
|
|
||||||
onProgress: () => {{}},
|
|
||||||
onFinish: () => {{}},
|
|
||||||
}});
|
|
||||||
const started = session.start(items[2]);
|
|
||||||
process.stdout.write(JSON.stringify({{started, opened}}));
|
|
||||||
"""
|
|
||||||
|
|
||||||
output = subprocess.run(
|
|
||||||
["node", "-e", script], check=True, capture_output=True, text=True
|
|
||||||
).stdout
|
|
||||||
assert json.loads(output) == {"started": True, "opened": [3]}
|
|
||||||
|
|
||||||
|
|
||||||
def test_my_work_filter_counts_distinguish_prs_from_review_requests():
|
def test_my_work_filter_counts_distinguish_prs_from_review_requests():
|
||||||
items = [
|
items = [
|
||||||
{"kind": "issue", "is_review": False},
|
{"kind": "issue", "is_review": False},
|
||||||
|
|
|
||||||
|
|
@ -101,5 +101,5 @@ async def test_mobile_dashboard_wires_focused_plan_today_sheet():
|
||||||
def test_plan_today_controller_is_available_in_the_offline_shell():
|
def test_plan_today_controller_is_available_in_the_offline_shell():
|
||||||
source = SERVICE_WORKER.read_text()
|
source = SERVICE_WORKER.read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/plan-today.js'" in source
|
assert "BASE + 'static/plan-today.js'" in source
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ async function dispatchNotificationClick(route) {{
|
||||||
def test_duplicate_aware_capture_ships_in_a_new_offline_shell():
|
def test_duplicate_aware_capture_ships_in_a_new_offline_shell():
|
||||||
source = WORKER.read_text()
|
source = WORKER.read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/create-issue-sheet.js'" in source
|
assert "BASE + 'static/create-issue-sheet.js'" in source
|
||||||
assert "BASE + 'static/dashboard.js'" in source
|
assert "BASE + 'static/dashboard.js'" in source
|
||||||
|
|
||||||
|
|
@ -116,14 +116,14 @@ def test_duplicate_aware_capture_ships_in_a_new_offline_shell():
|
||||||
def test_exact_later_picker_ships_atomically_in_a_new_offline_shell():
|
def test_exact_later_picker_ships_atomically_in_a_new_offline_shell():
|
||||||
source = WORKER.read_text()
|
source = WORKER.read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/later-picker.js'" in source
|
assert "BASE + 'static/later-picker.js'" in source
|
||||||
|
|
||||||
|
|
||||||
def test_navigation_deadline_ships_in_a_new_shell_cache():
|
def test_navigation_deadline_ships_in_a_new_shell_cache():
|
||||||
source = WORKER.read_text()
|
source = WORKER.read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/dashboard.css'" in source
|
assert "BASE + 'static/dashboard.css'" in source
|
||||||
assert "BASE + 'static/dashboard.js'" in source
|
assert "BASE + 'static/dashboard.js'" in source
|
||||||
assert "BASE + 'static/install-app.js'" in source
|
assert "BASE + 'static/install-app.js'" in source
|
||||||
|
|
@ -132,21 +132,21 @@ def test_navigation_deadline_ships_in_a_new_shell_cache():
|
||||||
def test_today_convergence_ships_in_a_new_shell_cache():
|
def test_today_convergence_ships_in_a_new_shell_cache():
|
||||||
source = WORKER.read_text()
|
source = WORKER.read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/today-sync.js'" in source
|
assert "BASE + 'static/today-sync.js'" in source
|
||||||
|
|
||||||
|
|
||||||
def test_mobile_search_viewport_ships_in_a_new_offline_shell():
|
def test_mobile_search_viewport_ships_in_a_new_offline_shell():
|
||||||
source = WORKER.read_text()
|
source = WORKER.read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/mobile-search-viewport.js'" in source
|
assert "BASE + 'static/mobile-search-viewport.js'" in source
|
||||||
|
|
||||||
|
|
||||||
def test_update_ownership_flow_ships_atomically_in_a_new_offline_shell():
|
def test_update_ownership_flow_ships_atomically_in_a_new_offline_shell():
|
||||||
source = WORKER.read_text()
|
source = WORKER.read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/update-ownership.js'" in source
|
assert "BASE + 'static/update-ownership.js'" in source
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -352,7 +352,6 @@ def test_install_precaches_complete_subpath_scoped_app_shell():
|
||||||
"/dashboard/static/conversation.js",
|
"/dashboard/static/conversation.js",
|
||||||
"/dashboard/static/issue-sheet.js",
|
"/dashboard/static/issue-sheet.js",
|
||||||
"/dashboard/static/create-issue-sheet.js",
|
"/dashboard/static/create-issue-sheet.js",
|
||||||
"/dashboard/static/create-and-start.js",
|
|
||||||
"/dashboard/static/pull-sheet.js",
|
"/dashboard/static/pull-sheet.js",
|
||||||
"/dashboard/static/review-sheet.js",
|
"/dashboard/static/review-sheet.js",
|
||||||
"/dashboard/static/work-route.js",
|
"/dashboard/static/work-route.js",
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ sync.enqueue('add', 'issue:r:1:');
|
||||||
def test_inflight_today_drain_ships_in_a_new_offline_shell():
|
def test_inflight_today_drain_ships_in_a_new_offline_shell():
|
||||||
source = (Path(__file__).parents[1] / "frontend" / "service-worker.js").read_text()
|
source = (Path(__file__).parents[1] / "frontend" / "service-worker.js").read_text()
|
||||||
|
|
||||||
assert "stackchain-dashboard-shell-v58" in source
|
assert "stackchain-dashboard-shell-v57" in source
|
||||||
assert "BASE + 'static/today-sync.js'" in source
|
assert "BASE + 'static/today-sync.js'" in source
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user