Stop polling idle agent update controls
CI / Test (rest) (pull_request) Successful in 22s
CI / Lint (pull_request) Successful in 11s
CI / Build (windows/amd64) (pull_request) Successful in 8s
CI / Test (store) (pull_request) Successful in 40s
CI / Build (linux/amd64) (pull_request) Successful in 7s
CI / Build (linux/arm64) (pull_request) Successful in 8s
CI / Test (server-http) (pull_request) Successful in 1m45s
e2e / Playwright vs docker-compose (pull_request) Successful in 1m26s
CI / Test (rest) (pull_request) Successful in 22s
CI / Lint (pull_request) Successful in 11s
CI / Build (windows/amd64) (pull_request) Successful in 8s
CI / Test (store) (pull_request) Successful in 40s
CI / Build (linux/amd64) (pull_request) Successful in 7s
CI / Build (linux/arm64) (pull_request) Successful in 8s
CI / Test (server-http) (pull_request) Successful in 1m45s
e2e / Playwright vs docker-compose (pull_request) Successful in 1m26s
This commit is contained in:
@@ -21,7 +21,7 @@ import (
|
|||||||
"gitea.dcglab.co.uk/steve/restic-manager/internal/version"
|
"gitea.dcglab.co.uk/steve/restic-manager/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFleetUpdatePagePreservesFiltersWithoutTypedConfirmation(t *testing.T) {
|
func TestFleetUpdateIdlePageDoesNotPollOrRequireTypedConfirmation(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
_, baseURL, st := newTestServerWithUI(t)
|
_, baseURL, st := newTestServerWithUI(t)
|
||||||
cookie := loginAsAdmin(t, st)
|
cookie := loginAsAdmin(t, st)
|
||||||
@@ -44,9 +44,6 @@ func TestFleetUpdatePagePreservesFiltersWithoutTypedConfirmation(t *testing.T) {
|
|||||||
`id="fleet-filter-name"`,
|
`id="fleet-filter-name"`,
|
||||||
`id="fleet-filter-version"`,
|
`id="fleet-filter-version"`,
|
||||||
`id="fleet-filter-state"`,
|
`id="fleet-filter-state"`,
|
||||||
"htmx:beforeSwap",
|
|
||||||
"fleetCaptureUIState()",
|
|
||||||
"fleetRestoreUIState()",
|
|
||||||
"r.style.display=",
|
"r.style.display=",
|
||||||
} {
|
} {
|
||||||
if !strings.Contains(body, want) {
|
if !strings.Contains(body, want) {
|
||||||
@@ -56,6 +53,33 @@ func TestFleetUpdatePagePreservesFiltersWithoutTypedConfirmation(t *testing.T) {
|
|||||||
if strings.Contains(body, "Type selected count to confirm") || strings.Contains(body, `id="fleet-update-confirm"`) {
|
if strings.Contains(body, "Type selected count to confirm") || strings.Contains(body, `id="fleet-update-confirm"`) {
|
||||||
t.Error("page still renders the typed-count confirmation")
|
t.Error("page still renders the typed-count confirmation")
|
||||||
}
|
}
|
||||||
|
if strings.Contains(body, `hx-trigger="every 3s`) {
|
||||||
|
t.Error("idle selection page must not poll and replace operator input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFleetUpdateRunningPagePollsForProgress(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, baseURL, st := newTestServerWithUI(t)
|
||||||
|
cookie, userID := loginAsAdminWithID(t, st)
|
||||||
|
hostID := makeHost(t, st, "fleet-running-host")
|
||||||
|
if err := st.CreateFleetUpdate(context.Background(), store.FleetUpdate{
|
||||||
|
ID: ulid.Make().String(), StartedByUserID: userID, TargetVersion: version.Version,
|
||||||
|
}, []string{hostID}); err != nil {
|
||||||
|
t.Fatalf("create fleet update: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, _ := stdhttp.NewRequest("GET", baseURL+"/settings/fleet-update", nil)
|
||||||
|
req.AddCookie(cookie)
|
||||||
|
res, err := stdhttp.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("get fleet update page: %v", err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
raw, _ := io.ReadAll(res.Body)
|
||||||
|
if body := string(raw); !strings.Contains(body, `hx-trigger="every 3s`) {
|
||||||
|
t.Error("running rollout page must poll for progress")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fakeFleetWorker stands in for *fleetupdate.Worker in HTTP tests.
|
// fakeFleetWorker stands in for *fleetupdate.Worker in HTTP tests.
|
||||||
|
|||||||
@@ -29,32 +29,12 @@
|
|||||||
{{template "fleet_update_inner" .}}
|
{{template "fleet_update_inner" .}}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let fleetUpdateUIState = null
|
|
||||||
|
|
||||||
function fleetBoxes(){return Array.from(document.querySelectorAll('.fleet-host:not(:disabled)'))}
|
function fleetBoxes(){return Array.from(document.querySelectorAll('.fleet-host:not(:disabled)'))}
|
||||||
function fleetReview(){const n=fleetBoxes().filter(x=>x.checked).length;document.getElementById('fleet-selected-count').textContent=n;document.getElementById('fleet-timeout').textContent=n+' × 95s';document.getElementById('fleet-update-start-btn').disabled=n===0}
|
function fleetReview(){const n=fleetBoxes().filter(x=>x.checked).length;document.getElementById('fleet-selected-count').textContent=n;document.getElementById('fleet-timeout').textContent=n+' × 95s';document.getElementById('fleet-update-start-btn').disabled=n===0}
|
||||||
function fleetFilter(){const q=document.getElementById('fleet-filter-name').value.toLowerCase(),v=document.getElementById('fleet-filter-version').value.toLowerCase(),s=document.getElementById('fleet-filter-state').value;document.querySelectorAll('.fleet-candidate').forEach(r=>r.style.display=r.dataset.name.toLowerCase().includes(q)&&r.dataset.version.toLowerCase().includes(v)&&(s==='all'||r.dataset.state===s)?'':'none')}
|
function fleetFilter(){const q=document.getElementById('fleet-filter-name').value.toLowerCase(),v=document.getElementById('fleet-filter-version').value.toLowerCase(),s=document.getElementById('fleet-filter-state').value;document.querySelectorAll('.fleet-candidate').forEach(r=>r.style.display=r.dataset.name.toLowerCase().includes(q)&&r.dataset.version.toLowerCase().includes(v)&&(s==='all'||r.dataset.state===s)?'':'none')}
|
||||||
function fleetSelectVisible(on){document.querySelectorAll('.fleet-candidate').forEach(r=>{const x=r.querySelector('.fleet-host:not(:disabled)');if(x&&r.style.display!=='none')x.checked=on});fleetReview()}
|
function fleetSelectVisible(on){document.querySelectorAll('.fleet-candidate').forEach(r=>{const x=r.querySelector('.fleet-host:not(:disabled)');if(x&&r.style.display!=='none')x.checked=on});fleetReview()}
|
||||||
function fleetSelectOnly(id){fleetBoxes().forEach(x=>x.checked=x.value===id);fleetReview()}
|
function fleetSelectOnly(id){fleetBoxes().forEach(x=>x.checked=x.value===id);fleetReview()}
|
||||||
async function fleetStart(e){e.preventDefault();const ids=fleetBoxes().filter(x=>x.checked).map(x=>x.value),out=document.getElementById('fleet-start-error');out.textContent='';const res=await fetch('/api/fleet/update',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({host_ids:ids,canary_first:document.getElementById('fleet-canary').checked})});if(res.ok){location.reload();return false}const body=await res.json();out.textContent=body.message||body.code||'Unable to start';return false}
|
async function fleetStart(e){e.preventDefault();const ids=fleetBoxes().filter(x=>x.checked).map(x=>x.value),out=document.getElementById('fleet-start-error');out.textContent='';const res=await fetch('/api/fleet/update',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({host_ids:ids,canary_first:document.getElementById('fleet-canary').checked})});if(res.ok){location.reload();return false}const body=await res.json();out.textContent=body.message||body.code||'Unable to start';return false}
|
||||||
|
|
||||||
function fleetCaptureUIState(){
|
|
||||||
const name=document.getElementById('fleet-filter-name'),version=document.getElementById('fleet-filter-version'),state=document.getElementById('fleet-filter-state'),canary=document.getElementById('fleet-canary')
|
|
||||||
if(!name)return
|
|
||||||
fleetUpdateUIState={name:name.value,version:version.value,state:state.value,canary:canary.checked,selected:fleetBoxes().filter(x=>x.checked).map(x=>x.value)}
|
|
||||||
}
|
|
||||||
function fleetRestoreUIState(){
|
|
||||||
if(!fleetUpdateUIState||!document.getElementById('fleet-filter-name'))return
|
|
||||||
document.getElementById('fleet-filter-name').value=fleetUpdateUIState.name
|
|
||||||
document.getElementById('fleet-filter-version').value=fleetUpdateUIState.version
|
|
||||||
document.getElementById('fleet-filter-state').value=fleetUpdateUIState.state
|
|
||||||
document.getElementById('fleet-canary').checked=fleetUpdateUIState.canary
|
|
||||||
const selected=new Set(fleetUpdateUIState.selected)
|
|
||||||
fleetBoxes().forEach(x=>x.checked=selected.has(x.value))
|
|
||||||
fleetFilter();fleetReview()
|
|
||||||
}
|
|
||||||
document.body.addEventListener('htmx:beforeSwap',e=>{if(e.detail.target.id==='fleet-update-panel')fleetCaptureUIState()})
|
|
||||||
document.body.addEventListener('htmx:afterSwap',e=>{if(e.detail.target.id==='fleet-update-panel')fleetRestoreUIState()})
|
|
||||||
if(document.getElementById('fleet-selected-count'))fleetReview()
|
if(document.getElementById('fleet-selected-count'))fleetReview()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
*/}}
|
*/}}
|
||||||
{{define "fleet_update_inner"}}
|
{{define "fleet_update_inner"}}
|
||||||
{{$page := .Page}}
|
{{$page := .Page}}
|
||||||
<div id="fleet-update-panel" class="mt-5"
|
<div id="fleet-update-panel" class="mt-5"{{if and $page.Active (eq $page.Active.Status "running")}}
|
||||||
hx-get="{{$page.PollURL}}"
|
hx-get="{{$page.PollURL}}"
|
||||||
hx-trigger="every 3s [document.visibilityState==='visible']"
|
hx-trigger="every 3s [document.visibilityState==='visible']"
|
||||||
hx-select="#fleet-update-panel"
|
hx-select="#fleet-update-panel"
|
||||||
hx-swap="outerHTML">
|
hx-swap="outerHTML"{{end}}>
|
||||||
|
|
||||||
{{if and $page.Active (eq $page.Active.Status "running")}}
|
{{if and $page.Active (eq $page.Active.Status "running")}}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user