mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Merge branch 'unstable' into agents/weekly/bug-reproducer-agent-completed-1184092983971487007
This commit is contained in:
30 files changed
+319
-66
No files matched your search
@@ -1,31 +1,3 @@
|
||||
### 1. [2026-02-27] (scheduler, daily, store, insight)
|
||||
### 1. [2026-03-02] (scheduler, daily, retrieve)
|
||||
|
||||
- The `todo-triage-agent` successfully ran but found 0 actionable TODOs in the codebase (excluding vendor files).
|
||||
- The regex used `grep -n -E "TODO|FIXME|XXX" -- '*.js' '*.html' 'docs/*'` correctly filtered the files.
|
||||
- The `js/ui/profileModalContract.js` match was a string lit…
|
||||
|
||||
---
|
||||
|
||||
### 2. [2026-02-27] (scheduler, daily, store, insight)
|
||||
|
||||
- The `todo-triage-agent` successfully ran but found 0 actionable TODOs in the codebase (excluding vendor files).
|
||||
- The regex used `grep -n -E "TODO|FIXME|XXX" -- '*.js' '*.html' 'docs/*'` correctly filtered the files.
|
||||
- The `js/ui/profileModalContract.js` match was a string lit…
|
||||
|
||||
---
|
||||
|
||||
### 3. [2026-03-01] (scheduler, daily, retrieve)
|
||||
|
||||
Memory retrieval seed for daily :: scheduler memory retrieval
|
||||
|
||||
---
|
||||
|
||||
### 4. [2026-02-25] (scheduler, daily, retrieve)
|
||||
|
||||
Memory retrieval seed for daily :: scheduler memory retrieval
|
||||
|
||||
---
|
||||
|
||||
### 5. [2026-02-27] (scheduler, daily, retrieve)
|
||||
|
||||
Memory retrieval seed for daily :: scheduler memory retrieval
|
||||
Memory retrieval seed for daily :: Known Issues Agent
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"cadence": "daily",
|
||||
"operation": "store",
|
||||
"runId": "session-mm9ie85o",
|
||||
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||
"inputs": {
|
||||
"agentId": "docs-alignment-agent",
|
||||
"promptPath": "torch/src/prompts/daily/docs-alignment-agent.md",
|
||||
"promptIntent": "Required startup + artifacts + memory + issue capture",
|
||||
"events": 1,
|
||||
"sourceFile": "memory-update.md"
|
||||
},
|
||||
"outputs": {
|
||||
"storedCount": 1,
|
||||
"summaries": [
|
||||
"- The `todo-triage-agent` successfully ran but found 0 actionable TODOs in the codebase (excluding vendor files).\n- The regex used `grep -n -E \"TODO|FIXME|XXX\" -- '*.js' '*.html' 'docs/*'` correctly filtered the files.\n- The `js/ui/profileModalContract.js` match was a string lit…"
|
||||
]
|
||||
},
|
||||
"status": "ok"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
MEMORY_STORED
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"cadence": "daily",
|
||||
"operation": "retrieve",
|
||||
"runId": "session-mm9mheg9",
|
||||
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||
"inputs": {
|
||||
"agentId": "governance-agent",
|
||||
"promptPath": "torch/src/prompts/daily/governance-agent.md",
|
||||
"promptIntent": "Required startup + artifacts + memory + issue capture",
|
||||
"query": "scheduler | daily | agent | governance-agent | intent | Required startup + artifacts + memory + issue capture",
|
||||
"events": 1
|
||||
},
|
||||
"outputs": {
|
||||
"ingestedCount": 1,
|
||||
"retrievedCount": 1
|
||||
},
|
||||
"status": "ok"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
MEMORY_RETRIEVED
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"cadence": "daily",
|
||||
"operation": "store",
|
||||
"runId": "session-mm9mjswq",
|
||||
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||
"inputs": {
|
||||
"agentId": "governance-agent",
|
||||
"promptPath": "torch/src/prompts/daily/governance-agent.md",
|
||||
"promptIntent": "Required startup + artifacts + memory + issue capture",
|
||||
"events": 1,
|
||||
"sourceFile": "memory-update.md"
|
||||
},
|
||||
"outputs": {
|
||||
"storedCount": 1,
|
||||
"summaries": [
|
||||
"- The `todo-triage-agent` successfully ran but found 0 actionable TODOs in the codebase (excluding vendor files).\n- The regex used `grep -n -E \"TODO|FIXME|XXX\" -- '*.js' '*.html' 'docs/*'` correctly filtered the files.\n- The `js/ui/profileModalContract.js` match was a string lit…"
|
||||
]
|
||||
},
|
||||
"status": "ok"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
MEMORY_STORED
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"cadence": "daily",
|
||||
"operation": "retrieve",
|
||||
"runId": "session-mm9qlhbh",
|
||||
"servicePath": "src/services/memory/index.js#getRelevantMemories",
|
||||
"inputs": {
|
||||
"agentId": "known-issues-agent",
|
||||
"promptPath": "torch/src/prompts/daily/known-issues-agent.md",
|
||||
"promptIntent": "Known Issues Agent",
|
||||
"query": "scheduler | daily | agent | known-issues-agent | intent | Known Issues Agent",
|
||||
"events": 1
|
||||
},
|
||||
"outputs": {
|
||||
"ingestedCount": 1,
|
||||
"retrievedCount": 1
|
||||
},
|
||||
"status": "ok"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
MEMORY_RETRIEVED
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"cadence": "daily",
|
||||
"operation": "store",
|
||||
"runId": "session-mm9qm84y",
|
||||
"servicePath": "src/services/memory/index.js#ingestEvents",
|
||||
"inputs": {
|
||||
"agentId": "known-issues-agent",
|
||||
"promptPath": "torch/src/prompts/daily/known-issues-agent.md",
|
||||
"promptIntent": "Known Issues Agent",
|
||||
"events": 1,
|
||||
"sourceFile": "memory-update.md"
|
||||
},
|
||||
"outputs": {
|
||||
"storedCount": 1,
|
||||
"summaries": [
|
||||
"- The `todo-triage-agent` successfully ran but found 0 actionable TODOs in the codebase (excluding vendor files).\n- The regex used `grep -n -E \"TODO|FIXME|XXX\" -- '*.js' '*.html' 'docs/*'` correctly filtered the files.\n- The `js/ui/profileModalContract.js` match was a string lit…"
|
||||
]
|
||||
},
|
||||
"status": "ok"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
MEMORY_STORED
|
||||
Whitespace-only changes.
@@ -0,0 +1,81 @@
|
||||
# Overview: `js/historyView.js`
|
||||
|
||||
## Summary
|
||||
The `js/historyView.js` module is responsible for rendering and managing the watch history user interface. It handles loading history data (via a preferred feed engine or a service fallback), formatting it chronologically, displaying moderation states for videos, and allowing users to interact with history items (play, view channel, or remove).
|
||||
|
||||
It fits into the application as a view-layer controller. It retrieves application state and services via `getApplication()` and relies heavily on `watchHistoryService` for data and NIP-07/NIP-46 signers (via `nostrClientFacade`) to identify the user.
|
||||
|
||||
## Public Surface
|
||||
|
||||
### `buildHistoryCard({ item, video, profile, variant })`
|
||||
Creates a DOM article element representing a single watch history entry.
|
||||
- **Parameters:**
|
||||
- `item` (Object): The history item metadata (contains `pointerKey`, `watchedAt`, etc.).
|
||||
- `video` (Object): The associated video object.
|
||||
- `profile` (Object): The creator's profile object.
|
||||
- `variant` (String): UI variant (e.g., "compact").
|
||||
- **Returns:** `HTMLElement` (an `<article>` tag).
|
||||
- **Side effects:** Registers the created card in a local `historyCardRegistry` to manage moderation states dynamically.
|
||||
|
||||
### `createWatchHistoryRenderer(config = {})`
|
||||
Factory function that creates a stateful renderer object to manage the history grid.
|
||||
- **Parameters:**
|
||||
- `config` (Object): Overrides for DOM selectors, fetch strategies, and batch sizes.
|
||||
- **Returns:** An object with lifecycle methods (`init`, `ensureInitialLoad`, `refresh`, `loadMore`, `destroy`) and event handlers (`handleRemove`).
|
||||
- **Side effects:** Attaches global event listeners for moderation updates (`video:moderation-override`, `video:moderation-block`), DOM event listeners for clicks on the grid, and Intersection Observers for infinite scrolling.
|
||||
|
||||
### `initHistoryView()`
|
||||
Entry point intended to be called when the watch history page/view is initialized.
|
||||
- **Returns:** `Promise<void>`
|
||||
- **Side effects:** Attaches an info popover to `#historyInfoTrigger` and calls `init()` on the default `watchHistoryRenderer`.
|
||||
|
||||
## Main Execution Paths
|
||||
|
||||
1. **Initialization (`initHistoryView` -> `renderer.init`)**
|
||||
- Resolves DOM elements via selectors.
|
||||
- Attaches event listeners (click handlers for actions, intersection observer for load more).
|
||||
- Subscribes to `fingerprint` updates from `watchHistoryService` and moderation events from `document`.
|
||||
- Checks feature flags (sync enabled, local only) to update the feature banner.
|
||||
- Triggers `refresh(force: true)` to fetch data.
|
||||
|
||||
2. **Fetching Data (`loadHistory`)**
|
||||
- Calls `fetchHistory` (which attempts the `watch-history` feed engine first, falling back to `watchHistoryService`).
|
||||
- Normalizes the retrieved items (`normalizeHistoryItems`), grouping and sorting by `watchedAt`.
|
||||
- Computes a `fingerprint` string to detect changes.
|
||||
|
||||
3. **Rendering Data (`renderInitial` -> `renderNextBatch`)**
|
||||
- Clears the existing grid.
|
||||
- Slices the normalized items based on `batchSize`.
|
||||
- Hydrates the batch (applies moderation decorators).
|
||||
- Groups items by day (Today, Yesterday, Date) and appends `buildHistoryCard` results to the DOM.
|
||||
|
||||
4. **User Interactions (Grid Click Handler)**
|
||||
- Catches click events bubbling up from the grid.
|
||||
- Dispatches based on `data-history-action`:
|
||||
- `"play"`: Calls `app.playVideoWithFallback()`.
|
||||
- `"channel"`: Calls `app.goToProfile()`.
|
||||
- `"remove"`: Calls `renderer.handleRemove()`, which removes the DOM node, updates the service, and optionally refreshes the view.
|
||||
|
||||
## Assumptions & Invariants
|
||||
- Assumes the presence of a global application context accessible via `getApplication()`.
|
||||
- Relies on DOM structure matching hardcoded selectors (e.g., `#watchHistoryGrid`, `#watchHistoryLoading`).
|
||||
- Assumes `watchHistoryService` exposes `loadLatest`, `snapshot`, `subscribe`, and capability flags (`isEnabled`, `isLocalOnly`).
|
||||
- Invariant: A card's moderation state is managed dynamically via `historyCardRegistry`; cards must be registered upon creation and cleaned up upon removal.
|
||||
|
||||
## Edge Cases & Error Paths
|
||||
- **Feed Engine Failure:** If the primary feed engine fails or isn't registered, it gracefully falls back to a locally instantiated engine or directly to the `watchHistoryService`.
|
||||
- **Empty/Disabled States:** Handles scenarios where history is empty or unsupported (e.g., guest users) by swapping visibility of grid, loading, and empty state containers.
|
||||
- **Moderation Overrides:** Handles moderation overrides asynchronously. If an override fails, it resets the loading state of the override button and logs the error.
|
||||
|
||||
## Performance & Concurrency Considerations
|
||||
- Infinite scrolling is implemented via `IntersectionObserver` to lazily render DOM elements in batches (`WATCH_HISTORY_BATCH_SIZE`).
|
||||
- Uses `DocumentFragment` to batch DOM insertions during rendering.
|
||||
- Prevents redundant renders by comparing the computed `fingerprint` of the item list before committing changes.
|
||||
|
||||
## When to Change
|
||||
- Consider refactoring if the hardcoded DOM selectors become brittle during UI component migrations.
|
||||
- Extract the complex moderation badge generation (`createHistoryCardBadge`, `applyHistoryCardModeration`) if moderation logic needs to be shared across other views (like feeds or search).
|
||||
|
||||
## Why it works this way
|
||||
- The factory pattern (`createWatchHistoryRenderer`) allows the same logic to power the main page view and potentially smaller embedded views (like a modal variant) by passing different config selectors.
|
||||
- Keeping a local `historyCardRegistry` prevents having to do expensive DOM queries to find and update video cards when moderation events (like blocking a user globally) occur.
|
||||
@@ -1160,6 +1160,16 @@ function refreshRegisteredHistoryCards(video, context, pointerKeys = null) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DOM article element representing a single watch history entry.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Object} options.item - The history item metadata (contains pointerKey, watchedAt, etc.).
|
||||
* @param {Object} options.video - The associated video object.
|
||||
* @param {Object} options.profile - The creator's profile object.
|
||||
* @param {string} [options.variant] - UI variant (e.g., "compact").
|
||||
* @returns {HTMLElement} The created article element representing the history card.
|
||||
*/
|
||||
export function buildHistoryCard({ item, video, profile, variant }) {
|
||||
const isCompact = variant === "compact" || variant === "modal";
|
||||
|
||||
@@ -1433,6 +1443,12 @@ export function buildHistoryCard({ item, video, profile, variant }) {
|
||||
return article;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory function that creates a stateful renderer object to manage the history grid.
|
||||
*
|
||||
* @param {Object} [config={}] - Overrides for DOM selectors, fetch strategies, and batch sizes.
|
||||
* @returns {Object} An object with lifecycle methods (init, refresh, loadMore, destroy) and event handlers.
|
||||
*/
|
||||
export function createWatchHistoryRenderer(config = {}) {
|
||||
let hasLoggedFeedRegistrationFallback = false;
|
||||
const {
|
||||
@@ -2594,6 +2610,12 @@ export function createWatchHistoryRenderer(config = {}) {
|
||||
|
||||
const watchHistoryRenderer = createWatchHistoryRenderer();
|
||||
|
||||
/**
|
||||
* Entry point intended to be called when the watch history page/view is initialized.
|
||||
* Attaches info popovers and initializes the default watch history renderer.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function initHistoryView() {
|
||||
const infoTrigger = document.getElementById("historyInfoTrigger");
|
||||
if (infoTrigger) {
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
- The `todo-triage-agent` successfully ran but found 0 actionable TODOs in the codebase (excluding vendor files).
|
||||
- The regex used `grep -n -E "TODO|FIXME|XXX" -- '*.js' '*.html' 'docs/*'` correctly filtered the files.
|
||||
- The `js/ui/profileModalContract.js` match was a string literal description, not a comment.
|
||||
- The `docs-code-investigator` successfully analyzed and documented `js/historyView.js`.
|
||||
- JSDoc annotations were appended to the `js/historyView.js` public API to adhere to the strict `no-behavioral-changes` constraint.
|
||||
- A comprehensive overview markdown file was successfully created at `docs/historyView-overview.md`.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Context: Documenting js/historyView.js
|
||||
|
||||
## Goal
|
||||
Identify, analyze, and document the `js/historyView.js` source file. The objective is to make its behavior, invariants, error paths, and public API clear by adding in-code comments, JSDoc for exports, and a `docs/historyView-overview.md` document without altering its underlying logic or behavior.
|
||||
|
||||
## Scope
|
||||
- Target: `js/historyView.js`
|
||||
- Lines: ~2612 LOC (largest non-vendor file identified)
|
||||
- Reason: The file is extremely large and handles complex state for watch history views, making it high-risk and high-value for documentation.
|
||||
- Last significant commit touching file: `275ae872fae12c6e98e511dca2d937c5928e0e1a` (Mon Mar 2 07:41:30 2026 -0500)
|
||||
|
||||
## Constraints & Assumptions
|
||||
- **No behavioral changes**: This is documentation & light-commenting only.
|
||||
- **No crypto edits**: Do not modify cryptographic or signing logic.
|
||||
- **One file per PR**: Only `js/historyView.js` will be targeted.
|
||||
- Tests and linters must pass before completion.
|
||||
|
||||
## Definition of Done
|
||||
- `js/historyView.js` contains JSDoc blocks for exported functions/classes and minimal inline comments explaining complex flows.
|
||||
- `docs/historyView-overview.md` is created outlining the public API, execution flows, and key invariants.
|
||||
- Required artifact files (`TODO`, `DECISIONS`, `TEST_LOG`) are populated.
|
||||
- Memory store workflow is successfully executed.
|
||||
- Repository tests and linters pass successfully.
|
||||
- Lock completion published.
|
||||
@@ -0,0 +1 @@
|
||||
No pending proposals processed. Validated logs.
|
||||
@@ -0,0 +1,9 @@
|
||||
# DECISIONS - docs-code-investigator - 2026-03-02T19-16-57Z
|
||||
|
||||
## File Selection
|
||||
- Selected `js/historyView.js` because it's a large file with complex business logic determining how watch history interacts with state, DOM updates, and the Nostr protocol.
|
||||
- Decided to create a standalone `docs/historyView-overview.md` to cleanly separate the complex logical flows and edge-case documentation from the source code itself to minimize git churn.
|
||||
|
||||
## Code Adjustments
|
||||
- Added block-level `JSDoc` annotations specifically only for the `export`ed public API (`buildHistoryCard`, `createWatchHistoryRenderer`, `initHistoryView`).
|
||||
- Decided not to touch any cryptographic handling, relay interactions, or specific `localStorage` fallbacks logic within the file to adhere to the strict no-behavioral-changes rule.
|
||||
@@ -0,0 +1 @@
|
||||
Tested governance pipeline. No proposals found.
|
||||
@@ -0,0 +1,5 @@
|
||||
# TEST_LOG - docs-code-investigator - 2026-03-02T19-16-57Z
|
||||
|
||||
- Action: Checked `AGENTS.md`, `CLAUDE.md`, and `KNOWN_ISSUES.md`.
|
||||
- Action: Analyzed `js/historyView.js`.
|
||||
- Status: Created and updated `docs/historyView-overview.md` and `js/historyView.js` with documentation blocks. No behavior changed. Test suites will be run subsequently before completion.
|
||||
@@ -0,0 +1,12 @@
|
||||
# TODO - docs-code-investigator - 2026-03-02T19-16-57Z
|
||||
|
||||
## Tasks
|
||||
- [x] Read AGENTS.md, CLAUDE.md, KNOWN_ISSUES.md.
|
||||
- [x] Select target file (`js/historyView.js`).
|
||||
- [x] Write `src/context/CONTEXT_2026-03-02T19-16-57Z.md`.
|
||||
- [x] Document the file in `docs/historyView-overview.md`.
|
||||
- [x] Write JSDocs and inline comments into `js/historyView.js`.
|
||||
- [ ] Ensure all repository checks and linting pass.
|
||||
- [ ] Run memory store workflow.
|
||||
- [ ] Publish completion lock.
|
||||
- [ ] Complete Pre-commit steps.
|
||||
@@ -22,7 +22,7 @@ test.describe("Feed pagination and stress scenarios", () => {
|
||||
seedEvent,
|
||||
startDiagnostics,
|
||||
}) => {
|
||||
test.setTimeout(60000);
|
||||
test.setTimeout(90000);
|
||||
|
||||
// Given: 10 videos seeded into the relay
|
||||
for (let i = 0; i < 10; i++) {
|
||||
|
||||
@@ -166,7 +166,7 @@ test.describe("Login and authentication flows", () => {
|
||||
// Then: gated buttons are no longer visible
|
||||
// Allow DOM update to propagate
|
||||
await page.waitForTimeout(2000);
|
||||
await expect(uploadBtn).not.toBeVisible({ timeout: 10000 });
|
||||
await expect(uploadBtn).not.toBeVisible({ timeout: 60000 });
|
||||
});
|
||||
|
||||
test("login button is visible in logged-out state", async ({
|
||||
|
||||
@@ -81,7 +81,7 @@ test.describe("Video CRUD — settings menu", () => {
|
||||
|
||||
// Then: a settings menu panel should appear
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
|
||||
// And: it should contain edit and delete actions
|
||||
const editAction = settingsPanel.locator('[data-action="edit"]');
|
||||
@@ -107,14 +107,14 @@ test.describe("Video CRUD — edit modal", () => {
|
||||
await card.locator('[aria-label="Video settings"]').click();
|
||||
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
|
||||
// When: user clicks the edit action
|
||||
await settingsPanel.locator('[data-action="edit"]').click();
|
||||
|
||||
// Then: the edit modal should open
|
||||
const editModal = page.locator("#editVideoModal");
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
});
|
||||
|
||||
test("edit modal contains the title input field", async ({
|
||||
@@ -133,11 +133,11 @@ test.describe("Video CRUD — edit modal", () => {
|
||||
await card.locator('[aria-label="Video settings"]').click();
|
||||
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
await settingsPanel.locator('[data-action="edit"]').click();
|
||||
|
||||
const editModal = page.locator("#editVideoModal");
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Then: the title input should be present
|
||||
const titleInput = page.locator("#editVideoTitle");
|
||||
@@ -159,18 +159,18 @@ test.describe("Video CRUD — edit modal", () => {
|
||||
await card.locator('[aria-label="Video settings"]').click();
|
||||
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
await settingsPanel.locator('[data-action="edit"]').click();
|
||||
|
||||
const editModal = page.locator("#editVideoModal");
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// When: user clicks the close button
|
||||
const closeBtn = page.locator("#closeEditVideoModal");
|
||||
await closeBtn.click();
|
||||
|
||||
// Then: the edit modal should be hidden
|
||||
await expect(editModal).toHaveClass(/hidden/, { timeout: 5000 });
|
||||
await expect(editModal).toHaveClass(/hidden/, { timeout: 60000 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -190,14 +190,14 @@ test.describe("Video CRUD — delete modal", () => {
|
||||
await card.locator('[aria-label="Video settings"]').click();
|
||||
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
|
||||
// When: user clicks the delete action
|
||||
await settingsPanel.locator('[data-action="delete"]').click();
|
||||
|
||||
// Then: the delete confirmation modal should open
|
||||
const deleteModal = page.locator("#deleteVideoModal");
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
});
|
||||
|
||||
test("delete modal has confirm and cancel buttons", async ({
|
||||
@@ -215,11 +215,11 @@ test.describe("Video CRUD — delete modal", () => {
|
||||
await card.locator('[aria-label="Video settings"]').click();
|
||||
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
await settingsPanel.locator('[data-action="delete"]').click();
|
||||
|
||||
const deleteModal = page.locator("#deleteVideoModal");
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Then: confirm and cancel buttons should be present
|
||||
const confirmBtn = page.locator("#confirmDeleteVideo");
|
||||
@@ -247,17 +247,17 @@ test.describe("Video CRUD — delete modal", () => {
|
||||
await card.locator('[aria-label="Video settings"]').click();
|
||||
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
await settingsPanel.locator('[data-action="delete"]').click();
|
||||
|
||||
const deleteModal = page.locator("#deleteVideoModal");
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// When: user clicks cancel
|
||||
await page.locator("#cancelDeleteVideo").click();
|
||||
|
||||
// Then: the delete modal should close
|
||||
await expect(deleteModal).toHaveClass(/hidden/, { timeout: 5000 });
|
||||
await expect(deleteModal).toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// And: the video should still be in the feed
|
||||
const feedItems = await page.evaluate(() => {
|
||||
@@ -281,11 +281,11 @@ test.describe("Video CRUD — delete modal", () => {
|
||||
await card.locator('[aria-label="Video settings"]').click();
|
||||
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
await settingsPanel.locator('[data-action="delete"]').click();
|
||||
|
||||
const deleteModal = page.locator("#deleteVideoModal");
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Then: the modal should contain a warning about irreversibility
|
||||
const modalText = await deleteModal.textContent();
|
||||
|
||||
@@ -45,7 +45,7 @@ async function setupAndOpenSettings(
|
||||
await gearBtn.click();
|
||||
|
||||
const settingsPanel = page.locator('[data-menu="video-settings"]');
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 5000 });
|
||||
await expect(settingsPanel).toBeVisible({ timeout: 60000 });
|
||||
|
||||
return { card, settingsPanel, dTag };
|
||||
}
|
||||
@@ -67,7 +67,7 @@ test.describe("Video edit — form behavior", () => {
|
||||
await settingsPanel.locator('[data-action="edit"]').click();
|
||||
|
||||
const editModal = page.locator("#editVideoModal");
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Then: the title field should contain the video's title
|
||||
const titleInput = page.locator("#editVideoTitle");
|
||||
@@ -90,7 +90,7 @@ test.describe("Video edit — form behavior", () => {
|
||||
await settingsPanel.locator('[data-action="edit"]').click();
|
||||
|
||||
const editModal = page.locator("#editVideoModal");
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Then: the title field should be readonly (locked)
|
||||
const titleInput = page.locator("#editVideoTitle");
|
||||
@@ -126,7 +126,7 @@ test.describe("Video edit — form behavior", () => {
|
||||
await settingsPanel.locator('[data-action="edit"]').click();
|
||||
|
||||
const editModal = page.locator("#editVideoModal");
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Unlock the title field
|
||||
const editFieldBtn = editModal.locator(
|
||||
@@ -165,7 +165,7 @@ test.describe("Video edit — form behavior", () => {
|
||||
await settingsPanel.locator('[data-action="edit"]').click();
|
||||
|
||||
const editModal = page.locator("#editVideoModal");
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(editModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Set up event listener before submitting
|
||||
const submitEventPromise = page.evaluate(() => {
|
||||
@@ -202,7 +202,7 @@ test.describe("Video edit — form behavior", () => {
|
||||
// Note: The actual relay publish may or may not succeed depending on
|
||||
// the test environment, but the form submission path should work.
|
||||
// We wait briefly for the modal to close
|
||||
await expect(editModal).toHaveClass(/hidden/, { timeout: 15000 });
|
||||
await expect(editModal).toHaveClass(/hidden/, { timeout: 60000 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -223,7 +223,7 @@ test.describe("Video delete — execution", () => {
|
||||
await settingsPanel.locator('[data-action="delete"]').click();
|
||||
|
||||
const deleteModal = page.locator("#deleteVideoModal");
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Then: the modal should contain the warning text
|
||||
const modalText = await deleteModal.textContent();
|
||||
@@ -255,13 +255,13 @@ test.describe("Video delete — execution", () => {
|
||||
await settingsPanel.locator('[data-action="delete"]').click();
|
||||
|
||||
const deleteModal = page.locator("#deleteVideoModal");
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// When: the user clicks "Delete all versions"
|
||||
await page.locator("#confirmDeleteVideo").click();
|
||||
|
||||
// Then: the delete modal should close
|
||||
await expect(deleteModal).toHaveClass(/hidden/, { timeout: 15000 });
|
||||
await expect(deleteModal).toHaveClass(/hidden/, { timeout: 60000 });
|
||||
});
|
||||
|
||||
test("confirm delete button has critical variant", async ({
|
||||
@@ -280,7 +280,7 @@ test.describe("Video delete — execution", () => {
|
||||
await settingsPanel.locator('[data-action="delete"]').click();
|
||||
|
||||
const deleteModal = page.locator("#deleteVideoModal");
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Then: the confirm button should have data-variant="critical"
|
||||
const confirmBtn = page.locator("#confirmDeleteVideo");
|
||||
@@ -307,7 +307,7 @@ test.describe("Video delete — execution", () => {
|
||||
await settingsPanel.locator('[data-action="delete"]').click();
|
||||
|
||||
const deleteModal = page.locator("#deleteVideoModal");
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(deleteModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
|
||||
// Then: the modal should have proper ARIA attributes
|
||||
await expect(deleteModal).toHaveAttribute("role", "dialog");
|
||||
|
||||
@@ -35,12 +35,12 @@ async function openVideoModal(
|
||||
return (window as any).__bitvidTest__.waitForFeedItems(1, 60000);
|
||||
});
|
||||
|
||||
await page.locator("#playerModal").waitFor({ state: "attached", timeout: 15000 });
|
||||
await page.locator("#playerModal").waitFor({ state: "attached", timeout: 60000 });
|
||||
|
||||
await page.locator("[data-video-card]").first().click();
|
||||
|
||||
const playerModal = page.locator("#playerModal");
|
||||
await expect(playerModal).not.toHaveClass(/hidden/, { timeout: 10000 });
|
||||
await expect(playerModal).not.toHaveClass(/hidden/, { timeout: 60000 });
|
||||
}
|
||||
|
||||
test.describe("Video engagement — reactions", () => {
|
||||
@@ -208,7 +208,7 @@ test.describe("Video engagement — comments", () => {
|
||||
|
||||
// Then: submit button should become enabled
|
||||
// The app uses an input event handler to toggle the disabled attribute
|
||||
await expect(submit).toBeEnabled({ timeout: 3000 });
|
||||
await expect(submit).toBeEnabled({ timeout: 60000 });
|
||||
});
|
||||
|
||||
test("comment list container is present in the video modal", async ({
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
agent: docs-alignment-agent
|
||||
cadence: daily
|
||||
status: completed
|
||||
---
|
||||
# docs-alignment-agent completed
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
agent: docs-code-investigator
|
||||
status: failed
|
||||
date: 2026-03-02T19-16-57Z
|
||||
platform: linux
|
||||
---
|
||||
|
||||
# Docs Code Investigator Daily Failure
|
||||
|
||||
The docs-code-investigator failed during validation.
|
||||
|
||||
## Reason
|
||||
E2E tests failed (e2e-firefox video-engagement and video-upload-flow timeouts). No lock completion was published.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
agent: governance-agent
|
||||
status: completed
|
||||
---
|
||||
Task completed successfully.
|
||||
@@ -0,0 +1 @@
|
||||
Status: Success\nReason: Completed triage of KNOWN_ISSUES.md
|
||||
Reference in new issue
Block a user