feat: добавить управление окружениями и Playwright для E2E

This commit is contained in:
sokol
2026-02-18 14:04:03 +03:00
parent 8f0112526f
commit 4b8f5f3739
11 changed files with 464 additions and 28 deletions

View File

@@ -1,10 +1,19 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { AddEvent, AppEvent, DelEvent, Env, UpdateEvent } from "../../models/Env";
import { EnvParam } from "../../models/EnvParam";
import { EnvironmentParam } from "./EnvironmentParam";
export function Environment(props: { envs: Env[], onChanged: (env: Env) => void, onSelected: (envId: number) => void }) {
const [currEnv, setCurrEnv] = useState(props.envs[0]);
export function Environment(props: { envs: Env[], onChanged: (env: Env) => void, onSelected: (envId: number) => void, onAdd: (env: Env) => number, onRemove: (envId: number) => void }) {
const [currEnvId, setCurrEnvId] = useState(props.envs[0]?.id);
// Sync currEnvId when props.envs changes
useEffect(() => {
if (!props.envs.find(e => e.id === currEnvId)) {
setCurrEnvId(props.envs[0]?.id);
}
}, [props.envs, currEnvId]);
const currEnv = props.envs.find(e => e.id === currEnvId) ?? props.envs[0];
function handleParamChanged(e: AppEvent<EnvParam>) {
let isChanged = false;
@@ -26,12 +35,43 @@ export function Environment(props: { envs: Env[], onChanged: (env: Env) => void,
}
if (isChanged) {
let idx = props.envs.findIndex(x => x.id === env.id);
if (idx > -1) {
props.envs[idx] = env;
props.onChanged(props.envs[idx]);
setCurrEnv(env);
props.onChanged(env);
setCurrEnvId(env.id);
}
}
function handleAddEnv() {
const name = prompt("Enter new environment name:");
if (!name || name.trim() === "") return;
const newEnv = new Env(
Math.random() * 10000,
name.trim(),
[...currEnv.params]
);
// Parent synchronously adds the env and returns the index
const newIdx = props.onAdd(newEnv);
setCurrEnvId(newEnv.id);
props.onSelected(newIdx);
}
function handleRemoveEnv() {
if (currEnv.isDefault()) {
alert("Cannot remove DEFAULT environment");
return;
}
if (!confirm(`Remove environment "${currEnv.name}"?`)) return;
const idx = props.envs.findIndex(x => x.id === currEnv.id);
if (idx > -1 && currEnv.id !== undefined) {
// Let parent handle the removal
props.onRemove(currEnv.id);
const newIdx = Math.max(0, idx - 1);
const newEnv = props.envs[newIdx];
if (newEnv?.id !== undefined) {
setCurrEnvId(newEnv.id);
}
props.onSelected(newIdx);
}
}
@@ -44,19 +84,28 @@ export function Environment(props: { envs: Env[], onChanged: (env: Env) => void,
return (
<>
<div className="row">
<select
id="environments"
name="environments"
aria-label="Environments"
className="form-select"
onChange={x => {
let id = Number.parseInt(x.target.value);
setCurrEnv(props.envs[id]);
props.onSelected(id);
}}>
{selectOptions}
</select>
<div className="row g-0">
<div className="col">
<select
id="environments"
name="environments"
aria-label="Environments"
className="form-select"
value={currEnvId}
onChange={x => {
let id = Number.parseInt(x.target.value);
setCurrEnvId(id);
props.onSelected(id);
}}>
{selectOptions}
</select>
</div>
<div className="col-auto ms-2">
<button className="btn btn-success" onClick={handleAddEnv} title="Add environment"></button>
</div>
<div className="col-auto ms-2">
<button className="btn btn-danger" onClick={handleRemoveEnv} title="Remove environment" disabled={currEnv.isDefault()}></button>
</div>
</div>
<div className="row">Params</div>
{paramCtrls}