Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ai/spec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ AI agents. Content is optimized for precision and machine consumption.
| Navigate the codebase | `how/project-structure.md` |
| Understand K8s data flow | `how/k8s-data-layer.md` |
| Understand the plugin system | `how/console-plugin-system.md` |
| Understand e2e testing | `how/e2e-testing.md` |
| Understand CI and e2e testing | `how/e2e-testing.md` |

## Cross-Reference

Expand Down
18 changes: 16 additions & 2 deletions .ai/spec/how/e2e-testing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# E2E Testing
# Testing

## Framework
## Konflux CI Pipeline

`.tekton/integration-tests/lightspeed-agentic-console-pre-commit.yaml` defines a Tekton
Pipeline that runs as a Konflux integration test on every PR. It checks out the PR commit
and runs three checks sequentially:

1. `npm run lint` — ESLint, Prettier, and Stylelint
2. `npm run test` — unit tests
3. `npm run i18n` — verifies locale files are up to date

The pipeline uses the Playwright base image (`mcr.microsoft.com/playwright`) for its Node.js
toolchain and runs with a 4Gi memory limit. It extracts the commit SHA from the Konflux
SNAPSHOT parameter to check out the exact PR revision.

## E2E Framework

Playwright with `@playwright/test`. Mirrors the setup used by the sibling
`lightspeed-console` repo.
Expand Down
3 changes: 3 additions & 0 deletions .ai/spec/how/project-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
| `integration-tests/support/fixtures.ts` | `test`, `oc`, `gatherClusterArtifacts` | Custom test fixture, cluster CLI helper, artifact collection |
| `integration-tests/support/global-setup.ts` | `globalSetup` | Operator readiness, browser login, storageState |
| `integration-tests/support/global-teardown.ts` | `globalTeardown` | Cluster cleanup and artifact gathering |
| `.tekton/lightspeed-agentic-console-pull-request.yaml` | — | Konflux PipelineRun for PR builds |
| `.tekton/lightspeed-agentic-console-push.yaml` | — | Konflux PipelineRun for push builds |
| `.tekton/integration-tests/lightspeed-agentic-console-pre-commit.yaml` | — | Konflux integration test Pipeline running lint, unit tests, and i18n checks |

## Key Entry Points

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: lightspeed-agentic-console-pre-commits
spec:
description: |
This pipeline runs pre-commit checks for OpenShift Lightspeed Agentic Console.
params:
- name: SNAPSHOT
description: 'The JSON string representing the snapshot of the application under test.'
default: '{"components": [{"name":"test-app", "containerImage": "quay.io/example/repo:latest", "source": {"git": {"revision": "main"}}}]}'
- name: test-name
description: 'The name of the test corresponding to a defined Konflux integration test.'
default: 'lightspeed-agentic-console-pre-commits'
tasks:
- name: extract-snapshot-metadata
description: Extract console image and commit from SNAPSHOT input
params:
- name: SNAPSHOT
value: $(params.SNAPSHOT)
taskSpec:
results:
- name: console-image
value: "$(steps.get-snapshot-images.results.console-image)"
- name: commit
value: "$(steps.get-snapshot-images.results.commit)"
params:
- name: SNAPSHOT
steps:
- name: get-snapshot-images
image: registry.redhat.io/openshift4/ose-cli:latest
env:
- name: SNAPSHOT
value: $(params.SNAPSHOT)
results:
- name: console-image
type: string
description: "console image from snapshot"
- name: commit
type: string
description: "commit sha to be used in console tests"
script: |
dnf -y install jq
echo -n "$(jq -r --arg component_name "lightspeed-agentic-console" '.components[] | select(.name == $component_name) | .containerImage' <<< "$SNAPSHOT")" > $(step.results.console-image.path)
echo -n "$(jq -r --arg component_name "lightspeed-agentic-console" '.components[] | select(.name == $component_name) | .source.git.revision' <<< "$SNAPSHOT")" > $(step.results.commit.path)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- name: lint
description: Run static checks
runAfter:
- extract-snapshot-metadata
params:
- name: commit
value: $(tasks.extract-snapshot-metadata.results.commit)
taskSpec:
params:
- name: commit
steps:
- name: run-lint
env:
- name: COMMIT_SHA
value: $(params.commit)
image: mcr.microsoft.com/playwright:v1.60.0-noble
resources:
limits:
memory: 4Gi
script: |
#!/bin/bash
set -euo pipefail
cd /home
git clone https://github.com/openshift/lightspeed-agentic-console.git
cd lightspeed-agentic-console
git fetch origin "${COMMIT_SHA}"
git config advice.detachedHead false
git checkout "${COMMIT_SHA}"
echo "---------------------------------------------"
echo "node version: $(node -v)"
echo "npm version: $(npm -v)"
echo "---------------------------------------------"
NODE_OPTIONS=--max-old-space-size=4096 npm ci --no-fund
echo "---------------------------------------------"
npm run lint
echo "---------------------------------------------"
npm run test
echo "---------------------------------------------"
npm run i18n
3 changes: 2 additions & 1 deletion locales/en/plugin__lightspeed-agentic-console-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
"After execution, a separate verification agent with read-only cluster access independently checks whether the fix worked. It inspects actual cluster state, not the execution agent's self-reported results. The checks shown below are the analysis agent's recommendations. The verification agent uses its own judgment based on what it observes, and every check it runs is reported transparently in the Verification tab. If verification fails, and retries were selected at approval, the Lightspeed operator will automatically retry execution with the failure reasons included as context for the execution agent.": "After execution, a separate verification agent with read-only cluster access independently checks whether the fix worked. It inspects actual cluster state, not the execution agent's self-reported results. The checks shown below are the analysis agent's recommendations. The verification agent uses its own judgment based on what it observes, and every check it runs is reported transparently in the Verification tab. If verification fails, and retries were selected at approval, the Lightspeed operator will automatically retry execution with the failure reasons included as context for the execution agent.",
"Age": "Age",
"Agent tiers define the model and settings used at each stage of a proposal workflow.": "Agent tiers define the model and settings used at each stage of a proposal workflow.",
"Agentic system is suspended": "Agentic system is suspended",
"Agents": "Agents",
"AI Hub": "AI Hub",
"Alert Diagnosis: {{name}}": "Alert Diagnosis: {{name}}",
"Alert rule created successfully": "Alert rule created successfully",
"All operations are halted and new proposals will be terminated. Remove or update the AgenticOLSConfig to resume.": "All operations are halted and new proposals will be terminated. Remove or update the AgenticOLSConfig to resume.",
"Analysis (seconds)": "Analysis (seconds)",
"Analysis did not complete — no proposal was generated.": "Analysis did not complete — no proposal was generated.",
"Analysis Sandbox": "Analysis Sandbox",
Expand All @@ -27,7 +29,6 @@
"Approve the escalation step for proposal {{name}}. The agent will research the issue, draft a support case, and file it.": "Approve the escalation step for proposal {{name}}. The agent will research the issue, draft a support case, and file it.",
"Approve Verification": "Approve Verification",
"Approve with {{num}} retries": "Approve with {{num}} retries",
"Approve with retries": "Approve with retries",
"Auto-scroll": "Auto-scroll",
"Automatic": "Automatic",
"Before": "Before",
Expand Down
1 change: 1 addition & 0 deletions src/components/configuration/AgentForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const AgentForm: React.FC<AgentFormProps> = ({ providers, onSubmit, onCancel })

React.useEffect(() => {
if (!providerName && providers.length > 0) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setProviderName(providers[0].metadata.name);
}
}, [providers, providerName]);
Expand Down
2 changes: 2 additions & 0 deletions src/components/configuration/ApprovalPolicyTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const ApprovalPolicyTab: React.FC = () => {
React.useEffect(() => {
if (policyExists) {
const stages = policy.spec?.stages;
// eslint-disable-next-line react-hooks/set-state-in-effect
setStageValues({
Analysis: getStageApproval(stages, 'Analysis'),
Execution: getStageApproval(stages, 'Execution'),
Expand All @@ -62,6 +63,7 @@ const ApprovalPolicyTab: React.FC = () => {
});
setMaxAttempts(policy.spec?.maxAttempts ?? 1);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [policyResourceVersion]);

const handleSave = async () => {
Expand Down
7 changes: 6 additions & 1 deletion src/components/proposals/ProposalDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,7 @@ const ProposalTab: React.FC<ProposalTabProps> = ({
);
React.useEffect(() => {
if (options.length === 1) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setLocalSelectedOption(0);
}
}, [options.length]);
Expand All @@ -984,6 +985,7 @@ const ProposalTab: React.FC<ProposalTabProps> = ({

const [execAgent, setExecAgent] = React.useState(proposal.spec.execution?.agent ?? '');
React.useEffect(() => {
// eslint-disable-next-line react-hooks/set-state-in-effect
setExecAgent(proposal.spec.execution?.agent ?? '');
}, [proposal.spec.execution?.agent]);

Expand Down Expand Up @@ -1885,6 +1887,8 @@ const ProposalDetailPage: React.FC = () => {
executionApproval.error ||
verificationApproval.error ||
escalationApproval.error;

/* eslint-disable react-hooks/exhaustive-deps */
const clearError = React.useCallback(() => {
analysisApproval.clearError();
executionApproval.clearError();
Expand All @@ -1896,6 +1900,7 @@ const ProposalDetailPage: React.FC = () => {
verificationApproval.clearError,
escalationApproval.clearError,
]);
/* eslint-enable react-hooks/exhaustive-deps */
const [escalateOpen, setEscalateOpen] = React.useState(false);

if (!loaded) {
Expand Down Expand Up @@ -2099,10 +2104,10 @@ const ProposalDetailPage: React.FC = () => {
>
{effectiveTab === 'overview' && (
<OverviewTab
proposal={proposal}
approval={approval}
latestExecutionResult={latestExecutionResult}
latestVerificationResult={latestVerificationResult}
proposal={proposal}
/>
)}
{effectiveTab === 'proposal' && (
Expand Down
2 changes: 2 additions & 0 deletions src/components/proposals/SandboxLogViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const SandboxLogViewer: React.FC<{

React.useEffect(() => {
if (!podExists) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setStatus((prev) => (prev === 'streaming' || prev === 'ended' ? 'ended' : 'searching'));
} else if (podPhase === 'Pending') {
setStatus('waiting');
Expand All @@ -91,6 +92,7 @@ const SandboxLogViewer: React.FC<{
return;
}

// eslint-disable-next-line react-hooks/set-state-in-effect
setStatus('streaming');
setLogs('');
const abortController = new AbortController();
Expand Down