fix(k1): keep reconnect feedback in place
This commit is contained in:
@@ -633,7 +633,7 @@ test("top-right device utility is an explicit pending-aware K1 scenario reset",
|
||||
/const modeResetInFlight = pendingAction === "mode" \|\| modeResetPending !== null/,
|
||||
);
|
||||
assert.match(provisioning, /if \(modeResetInFlight\) return/);
|
||||
assert.equal((provisioning.match(/disabled=\{modeResetInFlight\}/g) ?? []).length, 2);
|
||||
assert.equal((provisioning.match(/disabled=\{modeResetInFlight\}/g) ?? []).length, 1);
|
||||
assert.match(provisioning, /disabled=\{isBusy \|\| modeResetInFlight\}/);
|
||||
|
||||
assert.match(connection, /const hydratedScenarioResetKey = useRef<string \| null>\(null\)/);
|
||||
|
||||
@@ -19,6 +19,7 @@ let K1ProvisioningPipeline;
|
||||
let RuntimeActionFenceTestContext;
|
||||
let emptySearchPresentation;
|
||||
let emptyProvisioningAttemptPresentation;
|
||||
let emptyReadOnlyReconnectPresentation;
|
||||
let provisioningAttemptViewState;
|
||||
let unavailablePhysicalRetirementAuthority;
|
||||
let connectionAttemptOwnsAppliedNetworkRecovery;
|
||||
@@ -125,6 +126,7 @@ before(async () => {
|
||||
RuntimeActionFenceTestContext,
|
||||
emptySearchPresentation,
|
||||
emptyProvisioningAttemptPresentation,
|
||||
emptyReadOnlyReconnectPresentation,
|
||||
provisioningAttemptViewState,
|
||||
unavailablePhysicalRetirementAuthority,
|
||||
connectionAttemptOwnsAppliedNetworkRecovery,
|
||||
@@ -5228,11 +5230,21 @@ test("an unresolved physical command has one explicit server-bound read-only rec
|
||||
},
|
||||
desiredMode: "bridge",
|
||||
}, { publicSearch: false });
|
||||
assert.equal(
|
||||
(pendingMarkup.match(/class="connection-action-progress"/g) ?? []).length,
|
||||
1,
|
||||
const pendingReconnect = buttonMarkupWithText(
|
||||
pendingMarkup,
|
||||
"Переподключаемся…",
|
||||
);
|
||||
assert.doesNotMatch(pendingMarkup, /Переподключиться|Подключить новый K1|Подтверждаю:/);
|
||||
assert.equal(pendingReconnect.length, 1);
|
||||
assert.match(pendingReconnect[0], /disabled/);
|
||||
assert.match(pendingReconnect[0], /aria-busy="true"/);
|
||||
assert.match(pendingReconnect[0], /nodedc-activity-indicator/);
|
||||
const pendingConnectNew = buttonMarkupWithText(
|
||||
pendingMarkup,
|
||||
"Подключить новый K1",
|
||||
);
|
||||
assert.equal(pendingConnectNew.length, 1);
|
||||
assert.match(pendingConnectNew[0], /disabled/);
|
||||
assert.doesNotMatch(pendingMarkup, /connection-action-progress|Подтверждаю:/);
|
||||
|
||||
const source = readFileSync(provisioningSourceUrl, "utf8");
|
||||
const physicalRecovery = sourceSlice(
|
||||
@@ -5443,18 +5455,16 @@ test("unavailable physical replacement stays CAS-protected behind the simple new
|
||||
},
|
||||
desiredMode: "bridge",
|
||||
}, { publicSearch: false });
|
||||
assert.match(
|
||||
const bothPendingReconnect = buttonMarkupWithText(
|
||||
bothPendingMarkup,
|
||||
/Проверяем прежний K1 без повторения START, STOP или настроек сети/,
|
||||
);
|
||||
assert.equal(
|
||||
(bothPendingMarkup.match(/class="connection-action-progress"/g) ?? []).length,
|
||||
1,
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
bothPendingMarkup,
|
||||
/Переподключиться|Подключить новый K1|Подтверждаю:/,
|
||||
"Переподключаемся…",
|
||||
);
|
||||
assert.equal(bothPendingReconnect.length, 1);
|
||||
assert.match(bothPendingReconnect[0], /disabled/);
|
||||
assert.match(bothPendingReconnect[0], /aria-busy="true"/);
|
||||
assert.match(bothPendingReconnect[0], /nodedc-activity-indicator/);
|
||||
assert.match(bothPendingMarkup, /Прежнее подключение не подтверждено/);
|
||||
assert.doesNotMatch(bothPendingMarkup, /connection-action-progress|Подтверждаю:/);
|
||||
|
||||
const bothResolvedActive = structuredClone(bothAllowed);
|
||||
bothResolvedActive.physical_command.status = "resolved";
|
||||
@@ -6140,6 +6150,92 @@ test("unknown and cold durable recovery offer one reconnect and one new-device p
|
||||
assert.match(unrelatedMarkup, /Результат применения сети не подтверждён/);
|
||||
});
|
||||
|
||||
test("read-only reconnect keeps its card and loader across a transient projection", async () => {
|
||||
const initialState = terminalConnectionRecoveryState();
|
||||
initialState.connection_attempt = null;
|
||||
let currentState = initialState;
|
||||
let resolveVerification;
|
||||
let markVerificationStarted;
|
||||
const verificationStarted = new Promise((resolve) => {
|
||||
markVerificationStarted = resolve;
|
||||
});
|
||||
const controller = {
|
||||
...provisioningController(initialState),
|
||||
verifyConnection: async () => {
|
||||
markVerificationStarted();
|
||||
return new Promise((resolve) => {
|
||||
resolveVerification = resolve;
|
||||
});
|
||||
},
|
||||
getConnectionRecoveryObservationTarget: () =>
|
||||
recommendedConnectionRecoveryObservationTarget(currentState),
|
||||
isSnapshotRuntimeCurrent: (runtimeId) =>
|
||||
runtimeId === currentState.snapshot_runtime_id,
|
||||
};
|
||||
const props = () => ({
|
||||
controller: { ...controller, state: currentState },
|
||||
desiredMode: "bridge",
|
||||
});
|
||||
const harness = createStatefulProvisioningHarness(props());
|
||||
|
||||
try {
|
||||
let tree = harness.render(props());
|
||||
const reconnect = actionByLabel(tree, "Переподключиться");
|
||||
assert.ok(reconnect);
|
||||
reconnect.props.onClick();
|
||||
await Promise.race([
|
||||
verificationStarted,
|
||||
new Promise((_, reject) => setTimeout(
|
||||
() => reject(new Error("read-only reconnect did not dispatch")),
|
||||
100,
|
||||
)),
|
||||
]);
|
||||
|
||||
const transientState = structuredClone(initialState);
|
||||
transientState.connection_policy.allowed_actions =
|
||||
transientState.connection_policy.allowed_actions.filter(
|
||||
(action) => action !== "observe-configured-device-network",
|
||||
);
|
||||
delete transientState.connection_policy.actions[
|
||||
"observe-configured-device-network"
|
||||
];
|
||||
currentState = transientState;
|
||||
tree = harness.render(props());
|
||||
const pendingMarkup = renderToStaticMarkup(tree);
|
||||
const pendingReconnect = buttonMarkupWithText(
|
||||
pendingMarkup,
|
||||
"Переподключаемся…",
|
||||
);
|
||||
assert.equal(pendingReconnect.length, 1);
|
||||
assert.match(pendingReconnect[0], /disabled/);
|
||||
assert.match(pendingReconnect[0], /aria-busy="true"/);
|
||||
assert.match(pendingReconnect[0], /nodedc-activity-indicator/);
|
||||
assert.match(pendingMarkup, /Сохранённое подключение требует проверки/);
|
||||
assert.doesNotMatch(pendingMarkup, /connection-action-progress/);
|
||||
assert.equal(
|
||||
harness.stateValue(emptyReadOnlyReconnectPresentation)?.kind,
|
||||
"connection",
|
||||
);
|
||||
|
||||
currentState = initialState;
|
||||
resolveVerification({
|
||||
succeeded: false,
|
||||
reconciliationCompleted: false,
|
||||
observedState: initialState,
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
tree = harness.render(props());
|
||||
const failedMarkup = renderToStaticMarkup(tree);
|
||||
const retry = buttonMarkupWithText(failedMarkup, "Переподключиться");
|
||||
assert.equal(retry.length, 1);
|
||||
assert.doesNotMatch(retry[0], /disabled|aria-busy="true"/);
|
||||
assert.match(failedMarkup, /Проверьте питание и сеть, затем повторите проверку/);
|
||||
assert.equal(harness.stateValue(emptyReadOnlyReconnectPresentation), null);
|
||||
} finally {
|
||||
harness.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
test("cold saved new-device path resets once, performs zero Scan, and survives reload", async () => {
|
||||
const state = terminalConnectionRecoveryState();
|
||||
state.connection_attempt = null;
|
||||
@@ -6753,7 +6849,7 @@ test("applied-network recovery spends the old intent and admits only explicit se
|
||||
assert.match(source, /BLE-команда не повторяется/);
|
||||
assert.match(
|
||||
source,
|
||||
/status=\{\s*searchActive[\s\S]*?: physicalRecoveryRequired[\s\S]*?: networkRecoveryRequired[\s\S]*?>\s*\{searchActive \? \([\s\S]*?Поиск Bluetooth · \{scanSecondsRemaining \?\? 6\} с[\s\S]*?: networkRecoveryRequired && !connectionAttemptSettling \? \(/,
|
||||
/status=\{\s*searchActive[\s\S]*?: presentedPhysicalRecoveryRequired[\s\S]*?: networkRecoveryRequired[\s\S]*?>\s*\{searchActive \? \([\s\S]*?Поиск Bluetooth · \{scanSecondsRemaining \?\? 6\} с[\s\S]*?: networkRecoveryRequired && !connectionAttemptSettling \? \(/,
|
||||
);
|
||||
|
||||
assert.match(
|
||||
|
||||
Reference in New Issue
Block a user