mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-19 06:04:05 +00:00
refactor(automation): trim the branch to reduce risk
Adoption is low, and the app must not be put at risk fixing that. A measured look at the diff showed the risk was concentrated in exactly two places: the timer's hot path and a new file-writing route. Everything that actually makes automations discoverable — recipes, a panel that explains itself, one-step creation, Test buttons that report — is client-only settings-panel code with zero server churn. This cuts the two pieces that weren't, and simplifies two more that only existed to serve them. Removed entirely: templates. Newest, riskiest, and the least connected to the adoption problem — it helps someone who already uses automations share them, not someone who has never tried the feature. It was also the source of two of the bugs found in review. Removed: the websocket broadcast behind "last fired". reportFired ran inside triggerAutomations, called from onClock every second and from onUpdate. A new protocol message pushed to every connected client, including stage displays, up to once a second per automation, is real new traffic on the busiest code path in the app for a feature still trying to prove it's worth using. The log line stays: it's additive, throttled, nowhere near the hot path once written, and answers the same question — did this run — without a new message. Trimmed: the demo goes from two automations tied together by necessity — a danger-time message and a second automation whose only job was undoing the first on finish — to one, attached by an event-level trigger instead of a global one. Same two discovery wins, an automation visibly fires on Play and per-event triggers are found by opening an event, with no message mutation and nothing to keep in sync. That pairing was also the first thing review found broken. Trimmed: the delete dialog no longer deletes blocking triggers and restores them if the automation still won't delete. That rollback was the other multi-request destructive sequence review found a bug in. It now confirms, names what's blocking, and says to remove global triggers from the Global Triggers list first — a single always-safe request the user already has. What stays: one-step creation (lifecycles picked on the automation form), the recipe library, and the panel legibility work — none of it touches the server or the runtime. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LpbLJVVT26tzWkduck1M9H
This commit is contained in:
@@ -2,23 +2,12 @@ import { expect, test } from '@playwright/test';
|
||||
|
||||
const baseURL = 'http://localhost:4001';
|
||||
const automationsURL = `${baseURL}/data/automations`;
|
||||
const dbURL = `${baseURL}/data/db`;
|
||||
|
||||
const templateName = 'e2e-automations-template';
|
||||
|
||||
/**
|
||||
* Covers the loop that makes automations shareable:
|
||||
* create one, clone only the automations into a template project, and check that
|
||||
* the template carries the automation and its trigger while leaving the rundown behind.
|
||||
* Covers the automation delete flow: the server refuses to delete an automation that a
|
||||
* trigger still points at, and clears once the reference is removed.
|
||||
*/
|
||||
test.describe('automations', () => {
|
||||
/**
|
||||
* Everything created is torn down in afterEach rather than at the end of the test body:
|
||||
* a mid-test failure would otherwise leak an automation into the project, and CI retries twice.
|
||||
*
|
||||
* The template is tracked by the name the server actually used, since it resolves collisions.
|
||||
*/
|
||||
let createdTemplate: string | null = null;
|
||||
let createdTriggers: string[] = [];
|
||||
let createdAutomations: string[] = [];
|
||||
|
||||
@@ -31,63 +20,14 @@ test.describe('automations', () => {
|
||||
for (const id of createdAutomations) {
|
||||
await request.delete(`${automationsURL}/automation/${id}`);
|
||||
}
|
||||
if (createdTemplate !== null) {
|
||||
await request.delete(`${dbURL}/${createdTemplate}`);
|
||||
}
|
||||
} catch {
|
||||
// cleanup is best effort, it must not turn a passing test red
|
||||
} finally {
|
||||
createdTriggers = [];
|
||||
createdAutomations = [];
|
||||
createdTemplate = null;
|
||||
}
|
||||
});
|
||||
|
||||
test('an automation and its trigger survive a round trip through a template project', async ({ request }) => {
|
||||
// 1. create an automation
|
||||
const createAutomation = await request.post(`${automationsURL}/automation`, {
|
||||
data: {
|
||||
title: 'e2e automation',
|
||||
filterRule: 'all',
|
||||
filters: [],
|
||||
outputs: [{ type: 'ontime', action: 'aux1-start' }],
|
||||
},
|
||||
});
|
||||
expect(createAutomation.status()).toBe(201);
|
||||
const automation = await createAutomation.json();
|
||||
createdAutomations.push(automation.id);
|
||||
|
||||
// 2. bind it to a lifecycle
|
||||
const createTrigger = await request.post(`${automationsURL}/trigger`, {
|
||||
data: { title: 'e2e trigger', trigger: 'onStart', automationId: automation.id },
|
||||
});
|
||||
expect(createTrigger.status()).toBe(201);
|
||||
createdTriggers.push((await createTrigger.json()).id);
|
||||
|
||||
// 3. save the automations as a template, without touching the loaded project
|
||||
const projectList = await (await request.get(`${dbURL}/all`)).json();
|
||||
const currentProject = projectList.lastLoadedProject;
|
||||
|
||||
const makeTemplate = await request.post(`${dbURL}/${currentProject}/partial-duplicate`, {
|
||||
data: { newFilename: templateName, sections: ['automation'] },
|
||||
});
|
||||
expect(makeTemplate.status()).toBe(201);
|
||||
createdTemplate = (await makeTemplate.json()).filename;
|
||||
expect(createdTemplate).toBeTruthy();
|
||||
|
||||
// the running project is untouched
|
||||
const afterTemplate = await (await request.get(`${dbURL}/all`)).json();
|
||||
expect(afterTemplate.lastLoadedProject).toBe(currentProject);
|
||||
|
||||
// 4. the template carries the automation and its trigger, and nothing else
|
||||
const template = await (await request.post(`${dbURL}/download`, { data: { filename: createdTemplate } })).json();
|
||||
expect(Object.values(template.automation.automations)).toContainEqual(
|
||||
expect.objectContaining({ title: 'e2e automation' }),
|
||||
);
|
||||
expect(template.automation.triggers).toContainEqual(expect.objectContaining({ title: 'e2e trigger' }));
|
||||
expect(template.urlPresets).toEqual([]);
|
||||
});
|
||||
|
||||
test('refuses to delete an automation that a trigger still points at', async ({ request }) => {
|
||||
const automation = await (
|
||||
await request.post(`${automationsURL}/automation`, {
|
||||
|
||||
Reference in New Issue
Block a user