Compare commits
5 Commits
v1.0.6
...
759a1c745e
| Author | SHA1 | Date | |
|---|---|---|---|
| 759a1c745e | |||
| bb7c4c469c | |||
| 861b3dc9d4 | |||
| 8080a59de1 | |||
| a4e23aad11 |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
1
.gitignore
vendored
@@ -5,3 +5,4 @@ resources/
|
|||||||
frontend/vue-app/playwright-report/
|
frontend/vue-app/playwright-report/
|
||||||
frontend/vue-app/test-results/
|
frontend/vue-app/test-results/
|
||||||
backend/test-results/
|
backend/test-results/
|
||||||
|
.vscode/keybindings.json
|
||||||
|
|||||||
@@ -99,7 +99,11 @@ def _create_refresh_token(user_id: str, token_family: str | None = None) -> tupl
|
|||||||
def _set_auth_cookies(resp, access_token: str, raw_refresh_token: str):
|
def _set_auth_cookies(resp, access_token: str, raw_refresh_token: str):
|
||||||
"""Set both access and refresh token cookies on a response."""
|
"""Set both access and refresh token cookies on a response."""
|
||||||
expiry_days = current_app.config['REFRESH_TOKEN_EXPIRY_DAYS']
|
expiry_days = current_app.config['REFRESH_TOKEN_EXPIRY_DAYS']
|
||||||
resp.set_cookie('access_token', access_token, httponly=True, secure=True, samesite='Strict')
|
resp.set_cookie(
|
||||||
|
'access_token', access_token,
|
||||||
|
httponly=True, secure=True, samesite='Lax',
|
||||||
|
max_age=ACCESS_TOKEN_EXPIRY_MINUTES * 60,
|
||||||
|
)
|
||||||
resp.set_cookie(
|
resp.set_cookie(
|
||||||
'refresh_token', raw_refresh_token,
|
'refresh_token', raw_refresh_token,
|
||||||
httponly=True, secure=True, samesite='Strict',
|
httponly=True, secure=True, samesite='Strict',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from time import sleep
|
from time import sleep
|
||||||
from datetime import date as date_type, datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from tinydb import Query
|
from tinydb import Query
|
||||||
@@ -31,7 +31,7 @@ from models.tracking_event import TrackingEvent
|
|||||||
from utils.tracking_logger import log_tracking_event
|
from utils.tracking_logger import log_tracking_event
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from db.chore_schedules import get_schedule
|
from db.chore_schedules import get_schedule
|
||||||
from db.task_extensions import get_extension
|
from db.task_extensions import get_extension_for_child_task
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
child_api = Blueprint('child_api', __name__)
|
child_api = Blueprint('child_api', __name__)
|
||||||
@@ -288,8 +288,7 @@ def list_child_tasks(id):
|
|||||||
if task_obj.type == 'chore':
|
if task_obj.type == 'chore':
|
||||||
schedule = get_schedule(id, tid)
|
schedule = get_schedule(id, tid)
|
||||||
ct_dict['schedule'] = schedule.to_dict() if schedule else None
|
ct_dict['schedule'] = schedule.to_dict() if schedule else None
|
||||||
today_str = date_type.today().isoformat()
|
ext = get_extension_for_child_task(id, tid)
|
||||||
ext = get_extension(id, tid, today_str)
|
|
||||||
ct_dict['extension_date'] = ext.date if ext else None
|
ct_dict['extension_date'] = ext.date if ext else None
|
||||||
|
|
||||||
# Attach pending confirmation status for chores
|
# Attach pending confirmation status for chores
|
||||||
|
|||||||
@@ -160,11 +160,14 @@ def extend_chore_time(child_id, task_id):
|
|||||||
if not date or not isinstance(date, str):
|
if not date or not isinstance(date, str):
|
||||||
return jsonify({'error': 'date is required (ISO date string)', 'code': ErrorCodes.MISSING_FIELD}), 400
|
return jsonify({'error': 'date is required (ISO date string)', 'code': ErrorCodes.MISSING_FIELD}), 400
|
||||||
|
|
||||||
# 409 if already extended for this date
|
# 409 if already extended for this exact date
|
||||||
existing = get_extension(child_id, task_id, date)
|
existing = get_extension(child_id, task_id, date)
|
||||||
if existing:
|
if existing:
|
||||||
return jsonify({'error': 'Chore already extended for this date', 'code': 'ALREADY_EXTENDED'}), 409
|
return jsonify({'error': 'Chore already extended for this date', 'code': 'ALREADY_EXTENDED'}), 409
|
||||||
|
|
||||||
|
# Clear any prior extension for this child+task before inserting the new one
|
||||||
|
# so stale records from previous dates do not accumulate.
|
||||||
|
delete_extension_for_child_task(child_id, task_id)
|
||||||
extension = TaskExtension(child_id=child_id, task_id=task_id, date=date)
|
extension = TaskExtension(child_id=child_id, task_id=task_id, date=date)
|
||||||
add_extension(extension)
|
add_extension(extension)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# file: config/version.py
|
# file: config/version.py
|
||||||
import os
|
import os
|
||||||
|
|
||||||
BASE_VERSION = "1.0.6" # update manually when releasing features
|
BASE_VERSION = "1.0.9" # update manually when releasing features
|
||||||
|
|
||||||
def get_full_version() -> str:
|
def get_full_version() -> str:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
{
|
|
||||||
"_default": {
|
|
||||||
"1": {
|
|
||||||
"id": "57c21328-637e-4df3-be5b-7f619cbf4076",
|
|
||||||
"created_at": 1771343995.1881204,
|
|
||||||
"updated_at": 1771343995.188121,
|
|
||||||
"name": "Take out trash",
|
|
||||||
"points": 20,
|
|
||||||
"is_good": true,
|
|
||||||
"image_id": "trash-can",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"id": "70316500-e4ce-4399-8e4b-86a4046fafcb",
|
|
||||||
"created_at": 1771343995.1881304,
|
|
||||||
"updated_at": 1771343995.1881304,
|
|
||||||
"name": "Make your bed",
|
|
||||||
"points": 25,
|
|
||||||
"is_good": true,
|
|
||||||
"image_id": "make-the-bed",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"id": "71afb2e5-18de-4f99-9e1e-2f4e391e6c2c",
|
|
||||||
"created_at": 1771343995.1881359,
|
|
||||||
"updated_at": 1771343995.1881359,
|
|
||||||
"name": "Sweep and clean kitchen",
|
|
||||||
"points": 15,
|
|
||||||
"is_good": true,
|
|
||||||
"image_id": "vacuum",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"id": "e0aae53d-d4b6-4203-b910-004917db6003",
|
|
||||||
"created_at": 1771343995.1881409,
|
|
||||||
"updated_at": 1771343995.188141,
|
|
||||||
"name": "Do homework early",
|
|
||||||
"points": 30,
|
|
||||||
"is_good": true,
|
|
||||||
"image_id": "homework",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"id": "0ba544f6-2d61-4009-af8f-bcb4e94b7a11",
|
|
||||||
"created_at": 1771343995.188146,
|
|
||||||
"updated_at": 1771343995.188146,
|
|
||||||
"name": "Be good for the day",
|
|
||||||
"points": 15,
|
|
||||||
"is_good": true,
|
|
||||||
"image_id": "good",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"id": "8b5750d4-5a58-40cb-a31b-667569069d34",
|
|
||||||
"created_at": 1771343995.1881511,
|
|
||||||
"updated_at": 1771343995.1881511,
|
|
||||||
"name": "Clean your mess",
|
|
||||||
"points": 20,
|
|
||||||
"is_good": true,
|
|
||||||
"image_id": "broom",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"id": "aec5fb49-06d0-43c4-aa09-9583064b7275",
|
|
||||||
"created_at": 1771343995.1881557,
|
|
||||||
"updated_at": 1771343995.1881557,
|
|
||||||
"name": "Fighting",
|
|
||||||
"points": 10,
|
|
||||||
"is_good": false,
|
|
||||||
"image_id": "fighting",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"id": "0221ab72-c6c0-429f-a5f1-bc3d843fce9e",
|
|
||||||
"created_at": 1771343995.1881602,
|
|
||||||
"updated_at": 1771343995.1881602,
|
|
||||||
"name": "Yelling at parents",
|
|
||||||
"points": 10,
|
|
||||||
"is_good": false,
|
|
||||||
"image_id": "yelling",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"id": "672bfc74-4b85-4e8e-a2d0-74f14ab966cc",
|
|
||||||
"created_at": 1771343995.1881647,
|
|
||||||
"updated_at": 1771343995.1881647,
|
|
||||||
"name": "Lying",
|
|
||||||
"points": 10,
|
|
||||||
"is_good": false,
|
|
||||||
"image_id": "lying",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"id": "d8cc254f-922b-4dc2-ac4c-32fc3bbda584",
|
|
||||||
"created_at": 1771343995.1881692,
|
|
||||||
"updated_at": 1771343995.1881695,
|
|
||||||
"name": "Not doing what told",
|
|
||||||
"points": 5,
|
|
||||||
"is_good": false,
|
|
||||||
"image_id": "ignore",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"11": {
|
|
||||||
"id": "8be18d9a-48e6-402b-a0ba-630a2d50e325",
|
|
||||||
"created_at": 1771343995.188174,
|
|
||||||
"updated_at": 1771343995.188174,
|
|
||||||
"name": "Not flushing toilet",
|
|
||||||
"points": 5,
|
|
||||||
"is_good": false,
|
|
||||||
"image_id": "toilet",
|
|
||||||
"user_id": null
|
|
||||||
},
|
|
||||||
"12": {
|
|
||||||
"id": "b3b44115-529b-4eb3-9f8b-686dd24547a1",
|
|
||||||
"created_at": 1771345063.4665146,
|
|
||||||
"updated_at": 1771345063.4665148,
|
|
||||||
"name": "Take out trash",
|
|
||||||
"points": 21,
|
|
||||||
"is_good": true,
|
|
||||||
"image_id": "trash-can",
|
|
||||||
"user_id": "a5f05d38-7f7c-4663-b00f-3d6138e0e246"
|
|
||||||
},
|
|
||||||
"13": {
|
|
||||||
"id": "c74fc8c7-5af1-4d40-afbb-6da2647ca18b",
|
|
||||||
"created_at": 1771345069.1633172,
|
|
||||||
"updated_at": 1771345069.1633174,
|
|
||||||
"name": "aaa",
|
|
||||||
"points": 1,
|
|
||||||
"is_good": true,
|
|
||||||
"image_id": "computer-game",
|
|
||||||
"user_id": "a5f05d38-7f7c-4663-b00f-3d6138e0e246"
|
|
||||||
},
|
|
||||||
"14": {
|
|
||||||
"id": "65e79bbd-6cdf-4636-9e9d-f608206dbd80",
|
|
||||||
"created_at": 1772251855.4823341,
|
|
||||||
"updated_at": 1772251855.4823341,
|
|
||||||
"name": "Be Cool \ud83d\ude0e",
|
|
||||||
"points": 5,
|
|
||||||
"type": "kindness",
|
|
||||||
"image_id": "58d4adb9-3cee-4d7c-8e90-d81173716ce5",
|
|
||||||
"user_id": "6da06108-0db8-46be-b4cb-60ce7b54564d"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,3 +30,20 @@ def delete_extensions_for_task(task_id: str) -> None:
|
|||||||
def delete_extension_for_child_task(child_id: str, task_id: str) -> None:
|
def delete_extension_for_child_task(child_id: str, task_id: str) -> None:
|
||||||
q = Query()
|
q = Query()
|
||||||
task_extensions_db.remove((q.child_id == child_id) & (q.task_id == task_id))
|
task_extensions_db.remove((q.child_id == child_id) & (q.task_id == task_id))
|
||||||
|
|
||||||
|
|
||||||
|
def get_extension_for_child_task(child_id: str, task_id: str) -> TaskExtension | None:
|
||||||
|
"""Return the extension for a child+task without filtering by date.
|
||||||
|
|
||||||
|
Avoids timezone mismatches between the server (UTC) and the client's local
|
||||||
|
date. The caller (or the frontend) is responsible for deciding whether the
|
||||||
|
returned extension_date is still applicable.
|
||||||
|
"""
|
||||||
|
q = Query()
|
||||||
|
results = task_extensions_db.search(
|
||||||
|
(q.child_id == child_id) & (q.task_id == task_id)
|
||||||
|
)
|
||||||
|
if not results:
|
||||||
|
return None
|
||||||
|
latest = max(results, key=lambda r: r.get('date', ''))
|
||||||
|
return TaskExtension.from_dict(latest)
|
||||||
|
|||||||
@@ -439,6 +439,29 @@ def test_list_child_tasks_returns_extension_date_when_set(client):
|
|||||||
assert tasks[TASK_GOOD_ID]['extension_date'] == today
|
assert tasks[TASK_GOOD_ID]['extension_date'] == today
|
||||||
|
|
||||||
|
|
||||||
|
def test_list_child_tasks_returns_extension_date_for_yesterday(client):
|
||||||
|
"""Regression: extension inserted for yesterday's date must still be returned.
|
||||||
|
|
||||||
|
Previously, list-tasks used date_type.today() on the server to look up
|
||||||
|
extensions, causing a timezone mismatch when the client's local date
|
||||||
|
differed from the server's UTC date. The fix removes the date filter so
|
||||||
|
any stored extension is returned regardless of which date it was created
|
||||||
|
for.
|
||||||
|
"""
|
||||||
|
_setup_sched_child_and_tasks(task_db, child_db)
|
||||||
|
from datetime import date as date_type_local, timedelta
|
||||||
|
yesterday = (date_type_local.today() - timedelta(days=1)).isoformat()
|
||||||
|
task_extensions_db.insert({
|
||||||
|
'id': 'ext-yesterday',
|
||||||
|
'child_id': CHILD_SCHED_ID,
|
||||||
|
'task_id': TASK_GOOD_ID,
|
||||||
|
'date': yesterday,
|
||||||
|
})
|
||||||
|
resp = client.get(f'/child/{CHILD_SCHED_ID}/list-tasks')
|
||||||
|
tasks = {t['id']: t for t in resp.get_json()['tasks']}
|
||||||
|
assert tasks[TASK_GOOD_ID]['extension_date'] == yesterday
|
||||||
|
|
||||||
|
|
||||||
def test_list_child_tasks_extension_date_null_when_not_set(client):
|
def test_list_child_tasks_extension_date_null_when_not_set(client):
|
||||||
"""Good chore with no extension returns extension_date=null."""
|
"""Good chore with no extension returns extension_date=null."""
|
||||||
_setup_sched_child_and_tasks(task_db, child_db)
|
_setup_sched_child_and_tasks(task_db, child_db)
|
||||||
|
|||||||
4
frontend/test-results/.last-run.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"status": "failed",
|
||||||
|
"failedTests": []
|
||||||
|
}
|
||||||
@@ -2,23 +2,23 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"name": "refresh_token",
|
"name": "refresh_token",
|
||||||
"value": "oQxjeMmAW3v4aoI_hBdjpMXThVDzKZTG_6bQGPJBokc",
|
"value": "jvA7pfHaKU5t0mzLmz2tEsWpCPk92LNv8TiBbPqQPr4",
|
||||||
"domain": "localhost",
|
"domain": "localhost",
|
||||||
"path": "/api/auth",
|
"path": "/api/auth",
|
||||||
"expires": 1782271329.747941,
|
"expires": 1783780502.776691,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"sameSite": "Strict"
|
"sameSite": "Strict"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "access_token",
|
"name": "access_token",
|
||||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1jY0B0ZXN0LmNvbSIsInVzZXJfaWQiOiIyYjQ1MmRiNi1lYTRmLTQyMDItODIzYi0zNjllN2E3YTQ5MjYiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzQ0OTYyMjl9.docokOZYHgPtNNqirbfLSRABQHHsmTj-9_txuR8NHOM",
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1jY0B0ZXN0LmNvbSIsInVzZXJfaWQiOiI1YTI0NTY3Ny00YzRiLTRmMWUtODVjZi0zOWVkZmEwMjIwNjQiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzYwMDU0MDJ9.qPwnX1ZvCqaeuKblbD4y5CSNFfwDVSuR0ow_Eks2-DA",
|
||||||
"domain": "localhost",
|
"domain": "localhost",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": -1,
|
"expires": 1776005402.775975,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"sameSite": "Strict"
|
"sameSite": "Lax"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"origins": [
|
"origins": [
|
||||||
@@ -27,11 +27,11 @@
|
|||||||
"localStorage": [
|
"localStorage": [
|
||||||
{
|
{
|
||||||
"name": "authSyncEvent",
|
"name": "authSyncEvent",
|
||||||
"value": "{\"type\":\"logout\",\"at\":1774495329388}"
|
"value": "{\"type\":\"logout\",\"at\":1776004502483}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "parentAuth",
|
"name": "parentAuth",
|
||||||
"value": "{\"expiresAt\":1774668130069}"
|
"value": "{\"expiresAt\":1776177303116}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,23 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"name": "refresh_token",
|
"name": "refresh_token",
|
||||||
"value": "PcQvgm3e7SXL3CHGNXYGXhRrU7erXomAQDbOzVAUrCQ",
|
"value": "4eQQgcHp1arsl2u0kVDy5k19v75GcVFjYMGYNjSt9sE",
|
||||||
"domain": "localhost",
|
"domain": "localhost",
|
||||||
"path": "/api/auth",
|
"path": "/api/auth",
|
||||||
"expires": 1782271328.966396,
|
"expires": 1783780502.644833,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"sameSite": "Strict"
|
"sameSite": "Strict"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "access_token",
|
"name": "access_token",
|
||||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1kZWxldGVAdGVzdC5jb20iLCJ1c2VyX2lkIjoiOWM4ODk0MWUtMjhiMy00YzFmLWJhNDctMDNkMTU0MTc2ODA5IiwidG9rZW5fdmVyc2lvbiI6MCwiZXhwIjoxNzc0NDk2MjI4fQ.xxzCNQEX6y07hFJzEdumbYbhm_52VWM90tHsTPDfSKs",
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1kZWxldGVAdGVzdC5jb20iLCJ1c2VyX2lkIjoiZjNjZDUyOWMtZjFiNy00ZDNhLTlhNDYtMzAyYzg4YWQzNmUzIiwidG9rZW5fdmVyc2lvbiI6MCwiZXhwIjoxNzc2MDA1NDAyfQ.Y5NLYXHVRwNd4h5K1VwY9TrzaS-caMyaEey5xd4t5-0",
|
||||||
"domain": "localhost",
|
"domain": "localhost",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": -1,
|
"expires": 1776005402.6442,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"sameSite": "Strict"
|
"sameSite": "Lax"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"origins": [
|
"origins": [
|
||||||
@@ -27,11 +27,11 @@
|
|||||||
"localStorage": [
|
"localStorage": [
|
||||||
{
|
{
|
||||||
"name": "authSyncEvent",
|
"name": "authSyncEvent",
|
||||||
"value": "{\"type\":\"logout\",\"at\":1774495328432}"
|
"value": "{\"type\":\"logout\",\"at\":1776004502341}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "parentAuth",
|
"name": "parentAuth",
|
||||||
"value": "{\"expiresAt\":1774668129476}"
|
"value": "{\"expiresAt\":1776177303038}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,23 @@
|
|||||||
"cookies": [
|
"cookies": [
|
||||||
{
|
{
|
||||||
"name": "refresh_token",
|
"name": "refresh_token",
|
||||||
"value": "K9dRbu3607bZL23pmoMCD8qGM697tnpfY-dYVJ7qXPU",
|
"value": "ZV9WVFXOwIOdnTD5Xw3hSQAUmfpNdmVtDPpwz6IhCjc",
|
||||||
"domain": "localhost",
|
"domain": "localhost",
|
||||||
"path": "/api/auth",
|
"path": "/api/auth",
|
||||||
"expires": 1782271324.066253,
|
"expires": 1783780499.040639,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"sameSite": "Strict"
|
"sameSite": "Strict"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "access_token",
|
"name": "access_token",
|
||||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiJiMGFhNDczMC05ZDI4LTQ3NzQtODE3NC1kZTZmZWJkMjZjMmIiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzQ0OTYyMjR9.3RoSBnOU30YFn61lbfKo_ZMF2LQcsKb7qO31CK4AOl8",
|
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiJiOTQxMDVkZS1hNmIyLTQxMGEtODA1YS00YzhkYjc2NGU0ZWIiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzYwMDUzOTl9.XFDt0AnIgliNVOJOfNubRJgu1RI-LA2i95cfCwn6QEk",
|
||||||
"domain": "localhost",
|
"domain": "localhost",
|
||||||
"path": "/",
|
"path": "/",
|
||||||
"expires": -1,
|
"expires": 1776005399.040139,
|
||||||
"httpOnly": true,
|
"httpOnly": true,
|
||||||
"secure": true,
|
"secure": true,
|
||||||
"sameSite": "Strict"
|
"sameSite": "Lax"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"origins": [
|
"origins": [
|
||||||
@@ -27,11 +27,11 @@
|
|||||||
"localStorage": [
|
"localStorage": [
|
||||||
{
|
{
|
||||||
"name": "authSyncEvent",
|
"name": "authSyncEvent",
|
||||||
"value": "{\"type\":\"logout\",\"at\":1774495323817}"
|
"value": "{\"type\":\"logout\",\"at\":1776004498844}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "parentAuth",
|
"name": "parentAuth",
|
||||||
"value": "{\"expiresAt\":1774668124255}"
|
"value": "{\"expiresAt\":1776177299216}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,8 +128,9 @@ test.describe('Scroll to edited task after save', () => {
|
|||||||
await page.getByRole('button', { name: 'Save' }).click()
|
await page.getByRole('button', { name: 'Save' }).click()
|
||||||
|
|
||||||
// After SSE override event → list refreshes → card scrolls into view
|
// After SSE override event → list refreshes → card scrolls into view
|
||||||
|
// Use expect.poll to retry, allowing scroll animation to complete
|
||||||
await expect(targetCard).toBeVisible({ timeout: 5000 })
|
await expect(targetCard).toBeVisible({ timeout: 5000 })
|
||||||
expect(await isInViewport(targetCard)).toBe(true)
|
await expect.poll(() => isInViewport(targetCard), { timeout: 3000 }).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Edited reward card scrolls into viewport after override save', async ({ page }) => {
|
test('Edited reward card scrolls into viewport after override save', async ({ page }) => {
|
||||||
@@ -157,7 +158,8 @@ test.describe('Scroll to edited task after save', () => {
|
|||||||
await page.getByRole('button', { name: 'Save' }).click()
|
await page.getByRole('button', { name: 'Save' }).click()
|
||||||
|
|
||||||
// After SSE override event → list refreshes → card scrolls into view
|
// After SSE override event → list refreshes → card scrolls into view
|
||||||
|
// Use expect.poll to retry, allowing scroll animation to complete
|
||||||
await expect(targetCard).toBeVisible({ timeout: 5000 })
|
await expect(targetCard).toBeVisible({ timeout: 5000 })
|
||||||
expect(await isInViewport(targetCard)).toBe(true)
|
await expect.poll(() => isInViewport(targetCard), { timeout: 3000 }).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
BIN
frontend/vue-app/public/children.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
frontend/vue-app/public/chores.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
frontend/vue-app/public/notifications.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
frontend/vue-app/public/rewards.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
0
frontend/vue-app/pw-results.json
Normal file
@@ -1,5 +1,10 @@
|
|||||||
import { logoutUser } from '@/stores/auth'
|
import { logoutUser } from '@/stores/auth'
|
||||||
|
|
||||||
|
const hasLocalStorage =
|
||||||
|
typeof localStorage !== 'undefined' && typeof localStorage.getItem === 'function'
|
||||||
|
const CROSS_TAB_REFRESH_KEY = 'lastRefreshAt'
|
||||||
|
const CROSS_TAB_REFRESH_MIN_INTERVAL = 8_000 // 8s — prevents concurrent multi-tab refresh
|
||||||
|
|
||||||
let unauthorizedInterceptorInstalled = false
|
let unauthorizedInterceptorInstalled = false
|
||||||
let unauthorizedRedirectHandler: (() => void) | null = null
|
let unauthorizedRedirectHandler: (() => void) | null = null
|
||||||
let unauthorizedHandlingInProgress = false
|
let unauthorizedHandlingInProgress = false
|
||||||
@@ -15,6 +20,9 @@ export function setUnauthorizedRedirectHandlerForTests(handler: (() => void) | n
|
|||||||
export function resetInterceptorStateForTests(): void {
|
export function resetInterceptorStateForTests(): void {
|
||||||
unauthorizedHandlingInProgress = false
|
unauthorizedHandlingInProgress = false
|
||||||
refreshPromise = null
|
refreshPromise = null
|
||||||
|
if (hasLocalStorage && typeof localStorage.removeItem === 'function') {
|
||||||
|
localStorage.removeItem(CROSS_TAB_REFRESH_KEY)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUnauthorizedResponse(): void {
|
function handleUnauthorizedResponse(): void {
|
||||||
@@ -35,13 +43,27 @@ function handleUnauthorizedResponse(): void {
|
|||||||
* Attempt to refresh the access token by calling the refresh endpoint.
|
* Attempt to refresh the access token by calling the refresh endpoint.
|
||||||
* Returns true if refresh succeeded, false otherwise.
|
* Returns true if refresh succeeded, false otherwise.
|
||||||
* Uses a mutex so concurrent 401s only trigger one refresh call.
|
* Uses a mutex so concurrent 401s only trigger one refresh call.
|
||||||
|
* Also uses a localStorage timestamp to coordinate across tabs, preventing
|
||||||
|
* concurrent refreshes that would trigger false-positive theft detection.
|
||||||
*/
|
*/
|
||||||
async function attemptTokenRefresh(originalFetch: typeof fetch): Promise<boolean> {
|
async function attemptTokenRefresh(originalFetch: typeof fetch): Promise<boolean> {
|
||||||
|
// Cross-tab coordination: if another tab refreshed recently, the new cookie
|
||||||
|
// is already set. Skip the refresh call and let the original request retry.
|
||||||
|
if (hasLocalStorage && typeof localStorage.getItem === 'function') {
|
||||||
|
const lastRefresh = localStorage.getItem(CROSS_TAB_REFRESH_KEY)
|
||||||
|
if (lastRefresh && Date.now() - Number(lastRefresh) < CROSS_TAB_REFRESH_MIN_INTERVAL) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (refreshPromise) return refreshPromise
|
if (refreshPromise) return refreshPromise
|
||||||
|
|
||||||
refreshPromise = (async () => {
|
refreshPromise = (async () => {
|
||||||
try {
|
try {
|
||||||
const res = await originalFetch('/api/auth/refresh', { method: 'POST' })
|
const res = await originalFetch('/api/auth/refresh', { method: 'POST' })
|
||||||
|
if (res.ok && hasLocalStorage && typeof localStorage.setItem === 'function') {
|
||||||
|
localStorage.setItem(CROSS_TAB_REFRESH_KEY, String(Date.now()))
|
||||||
|
}
|
||||||
return res.ok
|
return res.ok
|
||||||
} catch {
|
} catch {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
import { ref, watch, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useBackendEvents } from '@/common/backendEvents'
|
import { useBackendEvents } from '@/common/backendEvents'
|
||||||
import { currentUserId, logoutUser, suppressForceLogout } from '@/stores/auth'
|
import { currentUserId, logoutParent, logoutUser, suppressForceLogout } from '@/stores/auth'
|
||||||
import { eventBus } from '@/common/eventBus'
|
import { eventBus } from '@/common/eventBus'
|
||||||
|
|
||||||
const userId = ref(currentUserId.value)
|
const userId = ref(currentUserId.value)
|
||||||
@@ -20,6 +20,7 @@ function handleForceLogout(event: { payload?: { reason?: string } }) {
|
|||||||
suppressForceLogout.value = false
|
suppressForceLogout.value = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
logoutParent()
|
||||||
logoutUser()
|
logoutUser()
|
||||||
if (event?.payload?.reason === 'account_deleted') {
|
if (event?.payload?.reason === 'account_deleted') {
|
||||||
router.push('/')
|
router.push('/')
|
||||||
|
|||||||
@@ -5,16 +5,20 @@ import BackendEventsListener from '../BackendEventsListener.vue'
|
|||||||
|
|
||||||
// ── Hoisted mocks ────────────────────────────────────────────────────────────
|
// ── Hoisted mocks ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
const { mockLogoutUser, mockSuppressForceLogout, mockCurrentUserId } = vi.hoisted(() => ({
|
const { mockLogoutUser, mockLogoutParent, mockSuppressForceLogout, mockCurrentUserId } = vi.hoisted(
|
||||||
|
() => ({
|
||||||
mockLogoutUser: vi.fn(),
|
mockLogoutUser: vi.fn(),
|
||||||
|
mockLogoutParent: vi.fn(),
|
||||||
mockSuppressForceLogout: { value: false },
|
mockSuppressForceLogout: { value: false },
|
||||||
mockCurrentUserId: { value: 'user-1' },
|
mockCurrentUserId: { value: 'user-1' },
|
||||||
}))
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
let capturedForceLogoutHandler: ((event: any) => void) | null = null
|
let capturedForceLogoutHandler: ((event: any) => void) | null = null
|
||||||
|
|
||||||
vi.mock('@/stores/auth', () => ({
|
vi.mock('@/stores/auth', () => ({
|
||||||
currentUserId: mockCurrentUserId,
|
currentUserId: mockCurrentUserId,
|
||||||
|
logoutParent: mockLogoutParent,
|
||||||
logoutUser: mockLogoutUser,
|
logoutUser: mockLogoutUser,
|
||||||
suppressForceLogout: mockSuppressForceLogout,
|
suppressForceLogout: mockSuppressForceLogout,
|
||||||
}))
|
}))
|
||||||
@@ -70,6 +74,7 @@ describe('BackendEventsListener – force_logout', () => {
|
|||||||
fireForceLogout('password_reset')
|
fireForceLogout('password_reset')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(mockLogoutParent).toHaveBeenCalledOnce()
|
||||||
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
||||||
expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
|
expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
|
||||||
})
|
})
|
||||||
@@ -81,6 +86,7 @@ describe('BackendEventsListener – force_logout', () => {
|
|||||||
fireForceLogout('account_deleted')
|
fireForceLogout('account_deleted')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(mockLogoutParent).toHaveBeenCalledOnce()
|
||||||
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
||||||
expect(pushSpy).toHaveBeenCalledWith('/')
|
expect(pushSpy).toHaveBeenCalledWith('/')
|
||||||
})
|
})
|
||||||
@@ -92,6 +98,7 @@ describe('BackendEventsListener – force_logout', () => {
|
|||||||
fireForceLogout('something_else')
|
fireForceLogout('something_else')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(mockLogoutParent).toHaveBeenCalledOnce()
|
||||||
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
||||||
expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
|
expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
|
||||||
})
|
})
|
||||||
@@ -103,6 +110,7 @@ describe('BackendEventsListener – force_logout', () => {
|
|||||||
fireForceLogout()
|
fireForceLogout()
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(mockLogoutParent).toHaveBeenCalledOnce()
|
||||||
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
||||||
expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
|
expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
|
||||||
})
|
})
|
||||||
@@ -115,6 +123,7 @@ describe('BackendEventsListener – force_logout', () => {
|
|||||||
fireForceLogout('password_reset')
|
fireForceLogout('password_reset')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(mockLogoutParent).not.toHaveBeenCalled()
|
||||||
expect(mockLogoutUser).not.toHaveBeenCalled()
|
expect(mockLogoutUser).not.toHaveBeenCalled()
|
||||||
expect(pushSpy).not.toHaveBeenCalled()
|
expect(pushSpy).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
@@ -142,6 +151,7 @@ describe('BackendEventsListener – force_logout', () => {
|
|||||||
// Second event: should go through normally
|
// Second event: should go through normally
|
||||||
fireForceLogout('password_reset')
|
fireForceLogout('password_reset')
|
||||||
await flushPromises()
|
await flushPromises()
|
||||||
|
expect(mockLogoutParent).toHaveBeenCalledOnce()
|
||||||
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
expect(mockLogoutUser).toHaveBeenCalledOnce()
|
||||||
expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
|
expect(pushSpy).toHaveBeenCalledWith({ name: 'Login' })
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ function executeMenuItem(index: number) {
|
|||||||
async function signOut() {
|
async function signOut() {
|
||||||
try {
|
try {
|
||||||
await fetch('/api/auth/logout', { method: 'POST' })
|
await fetch('/api/auth/logout', { method: 'POST' })
|
||||||
|
logoutParent()
|
||||||
logoutUser()
|
logoutUser()
|
||||||
router.push('/')
|
router.push('/')
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ vi.mock('@/stores/auth', () => ({
|
|||||||
isAuthReady: isAuthReadyMock,
|
isAuthReady: isAuthReadyMock,
|
||||||
isUserLoggedIn: isUserLoggedInMock,
|
isUserLoggedIn: isUserLoggedInMock,
|
||||||
isParentAuthenticated: isParentAuthenticatedMock,
|
isParentAuthenticated: isParentAuthenticatedMock,
|
||||||
logoutParent: vi.fn(),
|
|
||||||
enforceParentExpiry: vi.fn(),
|
enforceParentExpiry: vi.fn(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import {
|
|||||||
isUserLoggedIn,
|
isUserLoggedIn,
|
||||||
isParentAuthenticated,
|
isParentAuthenticated,
|
||||||
isAuthReady,
|
isAuthReady,
|
||||||
logoutParent,
|
|
||||||
enforceParentExpiry,
|
enforceParentExpiry,
|
||||||
} from '../stores/auth'
|
} from '../stores/auth'
|
||||||
import ParentPinSetup from '@/components/auth/ParentPinSetup.vue'
|
import ParentPinSetup from '@/components/auth/ParentPinSetup.vue'
|
||||||
@@ -304,8 +303,6 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
if (isParentAuthenticated.value) {
|
if (isParentAuthenticated.value) {
|
||||||
return next('/parent')
|
return next('/parent')
|
||||||
} else {
|
} else {
|
||||||
// Ensure parent auth is fully cleared when redirecting away from /parent
|
|
||||||
logoutParent()
|
|
||||||
return next('/child')
|
return next('/child')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,32 +12,8 @@ export const isParentAuthenticated = ref(false)
|
|||||||
export const isParentPersistent = ref(false)
|
export const isParentPersistent = ref(false)
|
||||||
export const parentAuthExpiresAt = ref<number | null>(null)
|
export const parentAuthExpiresAt = ref<number | null>(null)
|
||||||
|
|
||||||
// Restore persistent parent auth from localStorage on store init
|
|
||||||
if (hasLocalStorage) {
|
|
||||||
try {
|
|
||||||
const stored = localStorage.getItem(PARENT_AUTH_KEY)
|
|
||||||
if (stored) {
|
|
||||||
const parsed = JSON.parse(stored) as { expiresAt: number }
|
|
||||||
if (parsed.expiresAt && Date.now() < parsed.expiresAt) {
|
|
||||||
parentAuthExpiresAt.value = parsed.expiresAt
|
|
||||||
isParentPersistent.value = true
|
|
||||||
isParentAuthenticated.value = true
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(PARENT_AUTH_KEY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
localStorage.removeItem(PARENT_AUTH_KEY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isUserLoggedIn = ref(false)
|
|
||||||
export const isAuthReady = ref(false)
|
|
||||||
export const currentUserId = ref('')
|
|
||||||
export const suppressForceLogout = ref(false)
|
|
||||||
let authSyncInitialized = false
|
|
||||||
|
|
||||||
// --- Background expiry watcher ---
|
// --- Background expiry watcher ---
|
||||||
|
// Declared before the init block so startParentExpiryWatcher is safe to call during restore.
|
||||||
let expiryWatcherIntervalId: ReturnType<typeof setInterval> | null = null
|
let expiryWatcherIntervalId: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
function runExpiryCheck() {
|
function runExpiryCheck() {
|
||||||
@@ -61,6 +37,32 @@ export function stopParentExpiryWatcher() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore persistent parent auth from localStorage on store init
|
||||||
|
if (hasLocalStorage) {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(PARENT_AUTH_KEY)
|
||||||
|
if (stored) {
|
||||||
|
const parsed = JSON.parse(stored) as { expiresAt: number }
|
||||||
|
if (parsed.expiresAt && Date.now() < parsed.expiresAt) {
|
||||||
|
parentAuthExpiresAt.value = parsed.expiresAt
|
||||||
|
isParentPersistent.value = true
|
||||||
|
isParentAuthenticated.value = true
|
||||||
|
startParentExpiryWatcher()
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(PARENT_AUTH_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
localStorage.removeItem(PARENT_AUTH_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isUserLoggedIn = ref(false)
|
||||||
|
export const isAuthReady = ref(false)
|
||||||
|
export const currentUserId = ref('')
|
||||||
|
export const suppressForceLogout = ref(false)
|
||||||
|
let authSyncInitialized = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Explicitly checks whether parent auth has expired and clears it if so.
|
* Explicitly checks whether parent auth has expired and clears it if so.
|
||||||
* Called by the router guard before allowing /parent routes.
|
* Called by the router guard before allowing /parent routes.
|
||||||
@@ -81,6 +83,9 @@ export function authenticateParent(persistent: boolean) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function logoutParent() {
|
export function logoutParent() {
|
||||||
|
if (hasLocalStorage && typeof localStorage.removeItem === 'function') {
|
||||||
|
localStorage.removeItem(PARENT_AUTH_KEY)
|
||||||
|
}
|
||||||
applyParentLoggedOutState()
|
applyParentLoggedOutState()
|
||||||
broadcastParentLogoutEvent()
|
broadcastParentLogoutEvent()
|
||||||
}
|
}
|
||||||
@@ -89,9 +94,6 @@ function applyParentLoggedOutState() {
|
|||||||
parentAuthExpiresAt.value = null
|
parentAuthExpiresAt.value = null
|
||||||
isParentPersistent.value = false
|
isParentPersistent.value = false
|
||||||
isParentAuthenticated.value = false
|
isParentAuthenticated.value = false
|
||||||
if (hasLocalStorage && typeof localStorage.removeItem === 'function') {
|
|
||||||
localStorage.removeItem(PARENT_AUTH_KEY)
|
|
||||||
}
|
|
||||||
stopParentExpiryWatcher()
|
stopParentExpiryWatcher()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +107,11 @@ function broadcastParentLogoutEvent() {
|
|||||||
|
|
||||||
export function loginUser() {
|
export function loginUser() {
|
||||||
isUserLoggedIn.value = true
|
isUserLoggedIn.value = true
|
||||||
// Always start in child mode after login
|
// Always start in child mode after login. Also explicitly remove persistent parent
|
||||||
|
// auth from localStorage so a fresh login always starts in child mode.
|
||||||
|
if (hasLocalStorage && typeof localStorage.removeItem === 'function') {
|
||||||
|
localStorage.removeItem(PARENT_AUTH_KEY)
|
||||||
|
}
|
||||||
applyParentLoggedOutState()
|
applyParentLoggedOutState()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,10 +164,12 @@ export async function checkAuth() {
|
|||||||
currentUserId.value = data.id
|
currentUserId.value = data.id
|
||||||
isUserLoggedIn.value = true
|
isUserLoggedIn.value = true
|
||||||
} else {
|
} else {
|
||||||
logoutUser()
|
isUserLoggedIn.value = false
|
||||||
|
currentUserId.value = ''
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
logoutUser()
|
isUserLoggedIn.value = false
|
||||||
|
currentUserId.value = ''
|
||||||
}
|
}
|
||||||
isAuthReady.value = true
|
isAuthReady.value = true
|
||||||
}
|
}
|
||||||
|
|||||||
113
frontend/vue-app/targeted-test.txt
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
[1A[2K[2m[WebServer] [22m
|
||||||
|
[2m[WebServer] [22m> chore-app-frontend@0.0.0 dev
|
||||||
|
[2m[WebServer] [22m> vite
|
||||||
|
[2m[WebServer] [22m
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m
|
||||||
|
[2m[WebServer] [22m [32m[1mVITE[22m v7.2.2[39m [2mready in [0m[1m3718[22m[2m[0m ms[22m
|
||||||
|
[2m[WebServer] [22m
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m [32mΓ₧£[39m [1mLocal[22m: [36mhttps://localhost:[1m5173[22m/[39m
|
||||||
|
[2m[WebServer] [22m [32mΓ₧£[39m [1mNetwork[22m: [36mhttps://100.91.0.87:[1m5173[22m/[39m
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m [32mΓ₧£[39m [1mNetwork[22m: [36mhttps://192.168.1.102:[1m5173[22m/[39m
|
||||||
|
[2m[WebServer] [22m [32mΓ₧£[39m [1mNetwork[22m: [36mhttps://192.168.17.1:[1m5173[22m/[39m
|
||||||
|
[2m[WebServer] [22m [32mΓ₧£[39m [1mNetwork[22m: [36mhttps://192.168.44.1:[1m5173[22m/[39m
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22mPlatform: win32
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[WebServer] INFO:config.deletion_config:Account deletion threshold: 720 hours
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:24:51 - db.default - INFO - Initializing images. Ensuring directory exists: D:\Python Utilities\Reward\backend\test_data\images\default
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[WebServer] 2026-04-12 10:24:51,693 - account_deletion_scheduler - INFO - Starting account deletion scheduler
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:24:51 - account_deletion_scheduler - INFO - Starting account deletion scheduler
|
||||||
|
[2m[WebServer] [22m2026-04-12 10:24:51 - account_deletion_scheduler - INFO - Checking for interrupted deletions from previous runs
|
||||||
|
|
||||||
|
[1A[2K[WebServer] 2026-04-12 10:24:51,693 - account_deletion_scheduler - INFO - Checking for interrupted deletions from previous runs
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:24:51 - apscheduler.scheduler - INFO - Adding job tentatively -- it will be properly scheduled when the scheduler starts
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:24:51 - apscheduler.scheduler - INFO - Added job "Account Deletion Scheduler" to job store "default"
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:24:51 - apscheduler.scheduler - INFO - Scheduler started
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[WebServer] 2026-04-12 10:24:51,706 - account_deletion_scheduler - INFO - Account deletion scheduler started (runs every 1 hour)
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:24:51 - account_deletion_scheduler - INFO - Account deletion scheduler started (runs every 1 hour)
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m * Serving Flask app 'main.py'
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m * Debug mode: on
|
||||||
|
|
||||||
|
|
||||||
|
Running 19 tests using 6 workers
|
||||||
|
|
||||||
|
[1A[2K[1/19] [setup] › e2e\auth.setup.ts:4:1 › authenticate
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:24:54 - db.default - INFO - Initializing images. Ensuring directory exists: D:\Python Utilities\Reward\backend\test_data\images\default
|
||||||
|
|
||||||
|
[1A[2K[2/19] [chromium-task-modification] › e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:105:3 › Scroll to edited task after save › Edited chore card scrolls into viewport after override save
|
||||||
|
[1A[2K[3/19] [chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-cancel.spec.ts:32:3 › Penalty creation/edit cancellation › Cancel penalty edit
|
||||||
|
[1A[2K[4/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:26:3 › User Profile – Change Parent PIN › Change Parent PIN link navigates to PIN setup page
|
||||||
|
[1A[2K[5/19] [chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-cancel.spec.ts:11:3 › Penalty creation/edit cancellation › Cancel penalty creation
|
||||||
|
[1A[2K[6/19] [chromium-tasks-rewards] › e2e\mode_parent\notifications\notification-click-scroll.spec.ts:125:3 › Notification click navigates and scrolls to task › Clicking a chore notification navigates and scrolls to the chore card
|
||||||
|
[1A[2K[7/19] [chromium-tasks-rewards] › e2e\mode_parent\task-sorting\child-sort-order.spec.ts:179:3 › Child mode sort order › Child chore sort: scheduled today (earliest deadline) → general → pending
|
||||||
|
[1A[2K[8/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:35:3 › User Profile – Change Parent PIN › Back from PIN setup page returns to profile
|
||||||
|
[1A[2K[9/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:46:3 › User Profile – Change Parent PIN › Send Verification Code advances to step 2
|
||||||
|
[1A[2K[10/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:60:3 › User Profile – Change Parent PIN › Resend Code button appears after delay
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:08 - db.tracking - INFO - Tracking event created: confirmed chore eafea3ca-72e9-4286-b70c-9b205be8fe4f for child e55f3361-b7ef-4e2d-9318-86d8887702f6
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:09 - api.child_api - INFO - Child ChildSortChild has 50 points, reward CSortRewardPending costs 10 points
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:09 - api.child_api - INFO - Pending reward request created for child ChildSortChild for reward CSortRewardPending
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:09 - db.tracking - INFO - Tracking event created: requested reward dc7c5584-e425-4d21-ad53-5b0eb30597fb for child e55f3361-b7ef-4e2d-9318-86d8887702f6
|
||||||
|
|
||||||
|
[1A[2K[11/19] [chromium-tasks-rewards] › e2e\mode_parent\task-sorting\child-sort-order.spec.ts:214:3 › Child mode sort order › Child reward sort: pending first, then ascending by points needed
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:10 - db.tracking - INFO - Tracking event created: confirmed chore 1bcb0b2b-c1b9-4d48-b795-cd5b91f5ea2d for child a4bfda72-f28b-4da8-a0b9-7868fa78febd
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:11 - api.child_api - INFO - Child NotifScrollChild has 50 points, reward NotifScrollReward costs 10 points
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:11 - api.child_api - INFO - Pending reward request created for child NotifScrollChild for reward NotifScrollReward
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:11 - db.tracking - INFO - Tracking event created: requested reward ab631c51-d8bc-43f5-9fa9-5deeb0182351 for child a4bfda72-f28b-4da8-a0b9-7868fa78febd
|
||||||
|
|
||||||
|
[1A[2K[12/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:70:3 › User Profile – Change Parent PIN › Invalid code shows error
|
||||||
|
[1A[2K[13/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:81:3 › User Profile – Change Parent PIN › Valid code advances to step 3 (Create PIN)
|
||||||
|
[1A[2K[14/19] [chromium-tasks-rewards] › e2e\mode_parent\notifications\notification-click-scroll.spec.ts:149:3 › Notification click navigates and scrolls to task › Clicking a reward notification scrolls reward card into viewport
|
||||||
|
[1A[2K[15/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:99:3 › User Profile – Change Parent PIN › Set PIN disabled when PINs do not match
|
||||||
|
[1A[2K[16/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:116:3 › User Profile – Change Parent PIN › Set PIN disabled for fewer than 4 digits
|
||||||
|
[1A[2K[17/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:133:3 › User Profile – Change Parent PIN › Set PIN succeeds with matching 4-digit PIN – shows success step
|
||||||
|
[1A[2K[18/19] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:156:3 › User Profile – Change Parent PIN › Back from success step exits parent mode and goes to child selector
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:18 - db.child_overrides - INFO - Override created: child=8c6a8c65-f46c-4012-9d59-643dd09a28de, entity=179fda1f-2b7f-4c1d-82ce-ed152718b628, value=99
|
||||||
|
|
||||||
|
[1A[2K[19/19] [chromium-task-modification] › e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:135:3 › Scroll to edited task after save › Edited reward card scrolls into viewport after override save
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:19 - db.child_overrides - INFO - Override created: child=8c6a8c65-f46c-4012-9d59-643dd09a28de, entity=229028d2-efa7-49b5-a8f4-cd450ec40526, value=77
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:19 - db.child_overrides - INFO - Overrides cascade deleted for child: child_id=8c6a8c65-f46c-4012-9d59-643dd09a28de, count=2
|
||||||
|
|
||||||
|
[1A[2K[2m[WebServer] [22m2026-04-12 10:25:19 - api.child_api - INFO - Cascade deleted 2 overrides for child 8c6a8c65-f46c-4012-9d59-643dd09a28de
|
||||||
|
|
||||||
|
[1A[2K 1) [chromium-task-modification] › e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:135:3 › Scroll to edited task after save › Edited reward card scrolls into viewport after override save
|
||||||
|
|
||||||
|
Error: [2mexpect([22m[31mreceived[39m[2m).[22mtoBe[2m([22m[32mexpected[39m[2m) // Object.is equality[22m
|
||||||
|
|
||||||
|
Expected: [32mtrue[39m
|
||||||
|
Received: [31mfalse[39m
|
||||||
|
|
||||||
|
159 | // After SSE override event → list refreshes → card scrolls into view
|
||||||
|
160 | await expect(targetCard).toBeVisible({ timeout: 5000 })
|
||||||
|
> 161 | expect(await isInViewport(targetCard)).toBe(true)
|
||||||
|
| ^
|
||||||
|
162 | })
|
||||||
|
163 | })
|
||||||
|
164 |
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:161:44
|
||||||
|
|
||||||
|
Error Context: test-results\mode_parent-task-modificat-7e620-iewport-after-override-save-chromium-task-modification\error-context.md
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K 1 failed
|
||||||
|
[chromium-task-modification] › e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:135:3 › Scroll to edited task after save › Edited reward card scrolls into viewport after override save
|
||||||
|
18 passed (41.1s)
|
||||||
556
frontend/vue-app/test-output.txt
Normal file
@@ -0,0 +1,556 @@
|
|||||||
|
|
||||||
|
Running 287 tests using 6 workers
|
||||||
|
|
||||||
|
[1A[2K[1/287] [setup-no-pin] › e2e\auth-no-pin.setup.ts:11:7 › authenticate without parent pin
|
||||||
|
[1A[2K[2/287] [setup] › e2e\auth.setup.ts:4:1 › authenticate
|
||||||
|
[1A[2K[3/287] [chromium-child-options] › e2e\mode_parent\child-options\delete-child.spec.ts:23:3 › Child kebab menu – Delete Child › Confirm deletes the child from the list
|
||||||
|
[1A[2K[4/287] [setup-cc] › e2e\auth-cc.setup.ts:6:1 › authenticate create-child isolated user
|
||||||
|
[1A[2K[5/287] [setup-delete] › e2e\auth-delete.setup.ts:11:1 › authenticate delete user
|
||||||
|
[1A[2K[6/287] [chromium-child-options] › e2e\mode_parent\child-options\edit-child.spec.ts:24:3 › Child kebab menu – Edit Child › Edit Child menu item navigates to the child editor
|
||||||
|
[1A[2K[7/287] [chromium-default-tasks] › e2e\mode_parent\tasks\chore-convert-default.spec.ts:6:1 › Convert a default chore to a user item by editing
|
||||||
|
[1A[2K[8/287] [chromium-child-options] › e2e\mode_parent\child-options\delete-points.spec.ts:25:3 › Child kebab menu – Delete Points › Delete Points resets child points to 0
|
||||||
|
[1A[2K[9/287] [chromium-default-tasks] › e2e\mode_parent\tasks\chore-delete-default.spec.ts:6:1 › Edit default chore "Take out trash" and verify system chore restoration on delete
|
||||||
|
[1A[2K[10/287] [chromium-child-options] › e2e\mode_parent\child-options\delete-child.spec.ts:39:3 › Child kebab menu – Delete Child › Cancel does not delete the child
|
||||||
|
[1A[2K[11/287] [chromium-default-tasks] › e2e\mode_parent\tasks\penalty-default.spec.ts:28:3 › Default penalty management › Convert a default penalty to a user item by editing
|
||||||
|
[1A[2K[12/287] [chromium-default-tasks] › e2e\mode_parent\tasks\kindness-default.spec.ts:31:3 › Default kindness act management › Convert a default kindness act to a user item by editing
|
||||||
|
[1A[2K[13/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\assign-title-shows-child-name.spec.ts:28:3 › Assign view heading shows child name › "Assign Chores" heading contains child name
|
||||||
|
[1A[2K[14/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\assign-title-shows-child-name.spec.ts:35:3 › Assign view heading shows child name › "Assign Rewards" heading contains child name
|
||||||
|
[1A[2K[15/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\assign-title-shows-child-name.spec.ts:42:3 › Assign view heading shows child name › "Assign Kindness Acts" heading contains child name
|
||||||
|
[1A[2K[16/287] [chromium-default-tasks] › e2e\mode_parent\tasks\kindness-default.spec.ts:63:3 › Default kindness act management › Edit default kindness act "Be good for the day" and verify system act restoration on delete
|
||||||
|
[1A[2K[17/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\assign-title-shows-child-name.spec.ts:49:3 › Assign view heading shows child name › "Assign Penalties" heading contains child name
|
||||||
|
[1A[2K[18/287] [chromium-default-tasks] › e2e\mode_parent\tasks\penalty-default.spec.ts:65:3 › Default penalty management › Edit default penalty "Fighting" and verify system penalty restoration on delete
|
||||||
|
[1A[2K[19/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\chore-assignment.spec.ts:64:3 › Chore assignment › "Assign Chores" button navigates to the chore assign view
|
||||||
|
[1A[2K[20/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\kindness-assignment.spec.ts:54:3 › Kindness act assignment › "Assign Kindness Acts" button navigates to the kindness assign view
|
||||||
|
[1A[2K[21/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\penalty-assignment.spec.ts:64:3 › Penalty assignment › "Assign Penalties" button navigates to the penalty assign view
|
||||||
|
[1A[2K[22/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\reward-assignment.spec.ts:60:3 › Reward assignment › "Assign Rewards" button navigates to the reward assign view
|
||||||
|
[1A[2K[23/287] [chromium-task-activation] › e2e\mode_parent\task-activation\chore-activation.spec.ts:94:3 › Chore activation › Kebab button appears after first chore click
|
||||||
|
[1A[2K[24/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\kindness-assignment.spec.ts:72:3 › Kindness act assignment › Select a kindness act and submit — item appears in ParentView
|
||||||
|
[1A[2K[25/287] [chromium-task-activation] › e2e\mode_parent\task-activation\general-chore-no-completed.spec.ts:85:3 › General chore — no COMPLETED stamp › Approving a general chore awards points
|
||||||
|
[1A[2K[26/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\chore-assignment.spec.ts:83:3 › Chore assignment › Select chores and submit — items appear in ParentView chores section
|
||||||
|
[1A[2K[27/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\kindness-assignment.spec.ts:90:3 › Kindness act assignment › Re-opening assign view shows the assigned item as pre-checked
|
||||||
|
[1A[2K[28/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\penalty-assignment.spec.ts:83:3 › Penalty assignment › Select penalties and submit — items appear in ParentView penalties section
|
||||||
|
[1A[2K[29/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\kindness-assignment.spec.ts:99:3 › Kindness act assignment › Uncheck the kindness act and submit — item is removed from ParentView
|
||||||
|
[1A[2K[30/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\chore-assignment.spec.ts:108:3 › Chore assignment › Re-opening assign view shows assigned items as pre-checked
|
||||||
|
[1A[2K[31/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\chore-assignment.spec.ts:126:3 › Chore assignment › Uncheck all chores and submit — items are removed from ParentView
|
||||||
|
[1A[2K[32/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\penalty-assignment.spec.ts:110:3 › Penalty assignment › Re-opening assign view shows assigned penalties as pre-checked
|
||||||
|
[1A[2K[33/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\kindness-assignment.spec.ts:120:3 › Kindness act assignment › Cancel does not persist selection changes
|
||||||
|
[1A[2K[34/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\penalty-assignment.spec.ts:128:3 › Penalty assignment › Uncheck all penalties and submit — items are removed from ParentView
|
||||||
|
[1A[2K[35/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\reward-assignment.spec.ts:79:3 › Reward assignment › Select rewards and submit — items appear in ParentView rewards section
|
||||||
|
[1A[2K[36/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\chore-assignment.spec.ts:151:3 › Chore assignment › Cancel does not persist selection changes
|
||||||
|
[1A[2K[37/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\penalty-assignment.spec.ts:153:3 › Penalty assignment › Cancel does not persist selection changes
|
||||||
|
[1A[2K[38/287] [chromium-task-activation] › e2e\mode_parent\task-activation\chore-activation.spec.ts:108:3 › Chore activation › Kebab button disappears when clicking a non-chore item
|
||||||
|
[1A[2K[39/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\reward-assignment.spec.ts:106:3 › Reward assignment › Re-opening assign view shows assigned rewards as pre-checked
|
||||||
|
[1A[2K[40/287] [chromium-task-activation] › e2e\mode_parent\task-activation\chore-activation.spec.ts:125:3 › Chore activation › Cancel chore confirmation — no points awarded
|
||||||
|
[1A[2K[41/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\reward-assignment.spec.ts:124:3 › Reward assignment › Uncheck all rewards and submit — items are removed from ParentView
|
||||||
|
[1A[2K[42/287] [chromium-task-activation] › e2e\mode_parent\task-activation\general-chore-no-completed.spec.ts:99:3 › General chore — no COMPLETED stamp › General chore does NOT show COMPLETED stamp after approval
|
||||||
|
[1A[2K[43/287] [chromium-task-activation] › e2e\mode_parent\task-activation\kindness-activation.spec.ts:74:3 › Kindness act activation › Edit button appears on first kindness act click
|
||||||
|
[1A[2K[44/287] [chromium-task-assignment] › e2e\mode_parent\task-assignment\reward-assignment.spec.ts:149:3 › Reward assignment › Cancel does not persist selection changes
|
||||||
|
[1A[2K[45/287] [chromium-task-activation] › e2e\mode_parent\task-activation\general-chore-no-completed.spec.ts:107:3 › General chore — no COMPLETED stamp › General chore can be triggered again immediately after approval
|
||||||
|
[1A[2K[46/287] [chromium-task-activation] › e2e\mode_parent\task-activation\chore-activation.spec.ts:140:3 › Chore activation › Confirm chore activation — points awarded
|
||||||
|
[1A[2K[47/287] [chromium-task-activation] › e2e\mode_parent\task-activation\general-chore-no-completed.spec.ts:117:3 › General chore — no COMPLETED stamp › Scheduled chore DOES show COMPLETED after approval
|
||||||
|
[1A[2K[48/287] [chromium-task-activation] › e2e\mode_parent\task-activation\penalty-activation.spec.ts:81:3 › Penalty activation › Edit button appears on first penalty click
|
||||||
|
[1A[2K[49/287] [chromium-task-activation] › e2e\mode_parent\task-activation\chore-activation.spec.ts:154:3 › Chore activation › Confirmed chore shows COMPLETED stamp
|
||||||
|
[1A[2K[50/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:161:3 › Pending reward warning dialog › Pending reward card shows PENDING banner
|
||||||
|
[1A[2K[51/287] [chromium-task-activation] › e2e\mode_parent\task-activation\chore-activation.spec.ts:161:3 › Chore activation › Clicking a completed chore does not open confirmation
|
||||||
|
[1A[2K[52/287] [chromium-task-activation] › e2e\mode_parent\task-activation\kindness-activation.spec.ts:88:3 › Kindness act activation › Edit button disappears when clicking a different kindness card
|
||||||
|
[1A[2K[53/287] [chromium-task-activation] › e2e\mode_parent\task-activation\scroll-and-flow.spec.ts:76:3 › Scroll and flow › Kindness list is horizontally scrollable when many items are assigned
|
||||||
|
[1A[2K[54/287] [chromium-task-activation] › e2e\mode_parent\task-activation\reward-redemption.spec.ts:73:3 › Reward redemption › Locked reward shows correct points needed text
|
||||||
|
[1A[2K[55/287] [chromium-task-activation] › e2e\mode_parent\task-activation\kindness-activation.spec.ts:106:3 › Kindness act activation › Cancel kindness act confirmation — no points awarded
|
||||||
|
[1A[2K[56/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:83:3 › Chore edit points › Kebab button is hidden initially, visible after first click, hidden after deselect
|
||||||
|
[1A[2K[57/287] [chromium-task-activation] › e2e\mode_parent\task-activation\penalty-activation.spec.ts:95:3 › Penalty activation › Edit button disappears when clicking a different penalty card
|
||||||
|
[1A[2K[58/287] [chromium-task-activation] › e2e\mode_parent\task-activation\kindness-activation.spec.ts:121:3 › Kindness act activation › Confirm kindness act activation — points awarded
|
||||||
|
[1A[2K[59/287] [chromium-task-activation] › e2e\mode_parent\task-activation\penalty-activation.spec.ts:113:3 › Penalty activation › Cancel penalty confirmation — no points deducted
|
||||||
|
[1A[2K[60/287] [chromium-task-activation] › e2e\mode_parent\task-activation\kindness-activation.spec.ts:135:3 › Kindness act activation › Kindness act can be activated a second time — points awarded again
|
||||||
|
[1A[2K[61/287] [chromium-task-activation] › e2e\mode_parent\task-activation\penalty-activation.spec.ts:128:3 › Penalty activation › Confirm penalty activation — points deducted
|
||||||
|
[1A[2K[62/287] [chromium-task-activation] › e2e\mode_parent\task-activation\reward-redemption.spec.ts:83:3 › Reward redemption › Clicking a locked reward does not open confirmation dialog
|
||||||
|
[1A[2K[63/287] [chromium-task-activation] › e2e\mode_parent\task-activation\reward-redemption.spec.ts:98:3 › Reward redemption › Reward shows REWARD READY when child has sufficient points
|
||||||
|
[1A[2K[64/287] [chromium-task-activation] › e2e\mode_parent\task-activation\penalty-activation.spec.ts:144:3 › Penalty activation › Penalty can be activated multiple times — points deducted again
|
||||||
|
[1A[2K[65/287] [chromium-task-activation] › e2e\mode_parent\task-activation\reward-redemption.spec.ts:110:3 › Reward redemption › Edit button appears on first click of a ready reward
|
||||||
|
[1A[2K[66/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:106:3 › Chore edit points › Kebab menu contains "Edit Points" and "Schedule"
|
||||||
|
[1A[2K[67/287] [chromium-task-activation] › e2e\mode_parent\task-activation\reward-redemption.spec.ts:125:3 › Reward redemption › Edit button disappears when clicking a different reward card
|
||||||
|
[1A[2K[68/287] [chromium-task-activation] › e2e\mode_parent\task-activation\penalty-activation.spec.ts:160:3 › Penalty activation › Points floor at 0 — penalty cannot make points negative
|
||||||
|
[1A[2K[69/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:172:3 › Pending reward warning dialog › Chore activation — Cancel on pending dialog leaves state unchanged
|
||||||
|
[1A[2K[70/287] [chromium-task-activation] › e2e\mode_parent\task-activation\reward-redemption.spec.ts:144:3 › Reward redemption › Cancel reward redemption — points not deducted
|
||||||
|
[1A[2K[71/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:120:3 › Chore edit points › "Edit Points" opens override modal pre-populated with current points
|
||||||
|
[1A[2K[72/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-reset.spec.ts:90:3 › Chore reset › "Reset" is absent from kebab menu when chore is not yet completed
|
||||||
|
[1A[2K[73/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:136:3 › Chore edit points › Clicking outside the modal does not close it
|
||||||
|
[1A[2K[74/287] [chromium-task-activation] › e2e\mode_parent\task-activation\reward-redemption.spec.ts:162:3 › Reward redemption › Confirm reward redemption — points deducted and reward locks again
|
||||||
|
[1A[2K[75/287] [chromium-task-activation] › e2e\mode_parent\task-activation\scroll-and-flow.spec.ts:103:3 › Scroll and flow › Scrolling the list reveals hidden items
|
||||||
|
[1A[2K[76/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:196:3 › Pending reward warning dialog › Chore activation — Confirm on pending dialog cancels pending reward and triggers chore
|
||||||
|
[1A[2K[77/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:150:3 › Chore edit points › Cancel does not save — card still shows original points, no ⭐ badge
|
||||||
|
[1A[2K[78/287] [chromium-task-activation] › e2e\mode_parent\task-activation\scroll-and-flow.spec.ts:133:3 › Scroll and flow › First click on an off-screen item scrolls it into view and marks it ready
|
||||||
|
[1A[2K[79/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:164:3 › Chore edit points › Saving a valid new value updates the displayed points on the card
|
||||||
|
[1A[2K[80/287] [chromium-task-modification] › e2e\mode_parent\task-modification\kindness-edit-points.spec.ts:72:3 › Kindness act edit points › Edit button appears on first click of a kindness card
|
||||||
|
[1A[2K[81/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:222:3 › Pending reward warning dialog › Kindness activation — Cancel on pending dialog leaves state unchanged
|
||||||
|
[1A[2K[82/287] [chromium-task-activation] › e2e\mode_parent\task-activation\scroll-and-flow.spec.ts:163:3 › Scroll and flow › Scrolling does not prevent first-click centering on a partially visible item
|
||||||
|
[1A[2K[83/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:177:3 › Chore edit points › ⭐ override badge appears on the card after saving a custom value
|
||||||
|
[1A[2K[84/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:185:3 › Chore edit points › ⭐ badge persists after changing value back to the original default
|
||||||
|
[1A[2K[85/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-reset.spec.ts:100:3 › Chore reset › Chore shows COMPLETED stamp after activation and confirmation
|
||||||
|
[1A[2K[86/287] [chromium-task-modification] › e2e\mode_parent\task-modification\modified-points-activation.spec.ts:147:3 › Modified points take effect on activation › Modified chore points are awarded on activation (override 25, default 10)
|
||||||
|
[1A[2K[87/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:238:3 › Pending reward warning dialog › Kindness activation — Confirm on pending dialog cancels pending reward and triggers kindness
|
||||||
|
[1A[2K[88/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:200:3 › Chore edit points › Negative value disables the Save button
|
||||||
|
[1A[2K[89/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-reset.spec.ts:112:3 › Chore reset › "Reset" appears in kebab menu after chore is completed
|
||||||
|
[1A[2K[90/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:211:3 › Chore edit points › Value above 10000 disables Save; exactly 10000 enables it
|
||||||
|
[1A[2K[91/287] [chromium-task-modification] › e2e\mode_parent\task-modification\kindness-edit-points.spec.ts:83:3 › Kindness act edit points › Edit button disappears when clicking a different kindness card
|
||||||
|
[1A[2K[92/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-reset.spec.ts:122:3 › Chore reset › Clicking "Reset" removes the COMPLETED stamp
|
||||||
|
[1A[2K[93/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:261:3 › Pending reward warning dialog › Penalty activation — Cancel on pending dialog leaves state unchanged
|
||||||
|
[1A[2K[94/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-edit-points.spec.ts:224:3 › Chore edit points › Zero is a valid value — card shows "0 Points" with ⭐ badge
|
||||||
|
[1A[2K[95/287] [chromium-task-modification] › e2e\mode_parent\task-modification\kindness-edit-points.spec.ts:99:3 › Kindness act edit points › Edit button opens modal with kindness name and "Assign new points" subtitle
|
||||||
|
[1A[2K[96/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-reset.spec.ts:134:3 › Chore reset › Chore is re-activatable after reset — returns to COMPLETED on confirm
|
||||||
|
[1A[2K[97/287] [chromium-task-modification] › e2e\mode_parent\task-modification\kindness-edit-points.spec.ts:115:3 › Kindness act edit points › Input is pre-populated with the current point value
|
||||||
|
[1A[2K[98/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:277:3 › Pending reward warning dialog › Penalty activation — Confirm on pending dialog cancels pending reward and triggers penalty
|
||||||
|
[1A[2K[99/287] [chromium-task-modification] › e2e\mode_parent\task-modification\penalty-edit-points.spec.ts:72:3 › Penalty edit points › Edit button appears on first click of a penalty card
|
||||||
|
[1A[2K[100/287] [chromium-task-modification] › e2e\mode_parent\task-modification\kindness-edit-points.spec.ts:125:3 › Kindness act edit points › Cancel does not save — points remain unchanged, no ⭐ badge
|
||||||
|
[1A[2K[101/287] [chromium-task-modification] › e2e\mode_parent\task-modification\chore-reset.spec.ts:151:3 › Chore reset › Reset → activate cycle is repeatable without error
|
||||||
|
[1A[2K[102/287] [chromium-task-modification] › e2e\mode_parent\task-modification\override-sse-child-update.spec.ts:89:3 › Override SSE updates child view › Changing chore override points updates child view in real time
|
||||||
|
[1A[2K[103/287] [chromium-task-modification] › e2e\mode_parent\task-modification\kindness-edit-points.spec.ts:139:3 › Kindness act edit points › Saving a new value updates displayed points and shows ⭐ badge
|
||||||
|
[1A[2K[104/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:301:3 › Pending reward warning dialog › Other reward activation — Cancel on pending dialog leaves state unchanged
|
||||||
|
[1A[2K[105/287] [chromium-task-modification] › e2e\mode_parent\task-modification\kindness-edit-points.spec.ts:153:3 › Kindness act edit points › Negative value disables the Save button
|
||||||
|
[1A[2K[106/287] [chromium-task-modification] › e2e\mode_parent\task-modification\modified-points-activation.spec.ts:172:3 › Modified points take effect on activation › Modified kindness points are awarded on activation (override 8, default 10)
|
||||||
|
[1A[2K[107/287] [chromium-task-modification] › e2e\mode_parent\task-modification\penalty-edit-points.spec.ts:83:3 › Penalty edit points › Edit button disappears when clicking a different penalty card
|
||||||
|
[1A[2K[108/287] [chromium-task-modification] › e2e\mode_parent\task-modification\pending-reset-on-edit.spec.ts:107:3 › Pending status resets on task/reward modification › 10.1 Editing chore points clears pending status
|
||||||
|
[1A[2K[109/287] [chromium-task-modification] › e2e\mode_parent\task-modification\kindness-edit-points.spec.ts:164:3 › Kindness act edit points › Value above 10000 disables Save; exactly 10000 enables it
|
||||||
|
[1A[2K[110/287] [chromium-task-activation] › e2e\mode_parent\task-activation\pending-reward-warning.spec.ts:333:3 › Pending reward warning dialog › Other reward activation — Confirm on pending dialog cancels the pending reward
|
||||||
|
[1A[2K[111/287] [chromium-task-modification] › e2e\mode_parent\task-modification\penalty-edit-points.spec.ts:99:3 › Penalty edit points › Edit button opens modal with penalty name and "Assign new points" subtitle
|
||||||
|
[1A[2K[112/287] [chromium-task-modification] › e2e\mode_parent\task-modification\penalty-edit-points.spec.ts:115:3 › Penalty edit points › Input is pre-populated with the raw point value (positive, not negated)
|
||||||
|
[1A[2K[113/287] [chromium-task-modification] › e2e\mode_parent\task-modification\modified-points-activation.spec.ts:197:3 › Modified points take effect on activation › Modified penalty points are deducted at override rate (override 3, default 10)
|
||||||
|
[1A[2K[114/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:67:3 › Reward edit cost › Edit button appears on first click of a reward card
|
||||||
|
[1A[2K[115/287] [chromium-task-modification] › e2e\mode_parent\task-modification\penalty-edit-points.spec.ts:128:3 › Penalty edit points › Cancel does not change the display
|
||||||
|
[1A[2K[116/287] [chromium-task-modification] › e2e\mode_parent\task-modification\override-sse-child-update.spec.ts:109:3 › Override SSE updates child view › Changing kindness override points updates child view
|
||||||
|
[1A[2K[117/287] [chromium-task-modification] › e2e\mode_parent\task-modification\penalty-edit-points.spec.ts:143:3 › Penalty edit points › Saving a new value updates card to show negated override and ⭐ badge
|
||||||
|
[1A[2K[118/287] [chromium-task-modification] › e2e\mode_parent\task-modification\modified-points-activation.spec.ts:222:3 › Modified points take effect on activation › Modified reward cost is reflected in points needed display (override 30)
|
||||||
|
[1A[2K[119/287] [chromium-task-modification] › e2e\mode_parent\task-modification\override-sse-child-update.spec.ts:123:3 › Override SSE updates child view › Changing penalty override points updates child view
|
||||||
|
[1A[2K[120/287] [chromium-task-modification] › e2e\mode_parent\task-modification\penalty-edit-points.spec.ts:159:3 › Penalty edit points › Negative value disables the Save button
|
||||||
|
[1A[2K[121/287] [chromium-task-modification] › e2e\mode_parent\task-modification\override-sse-child-update.spec.ts:137:3 › Override SSE updates child view › Deleting an override reverts child view to original points
|
||||||
|
[1A[2K[122/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:78:3 › Reward edit cost › Edit button opens modal with reward name and "Assign new cost" subtitle
|
||||||
|
[1A[2K[123/287] [chromium-task-modification] › e2e\mode_parent\task-modification\pending-reset-on-edit.spec.ts:128:3 › Pending status resets on task/reward modification › 10.2 Changing chore schedule clears pending status
|
||||||
|
[1A[2K[124/287] [chromium-task-modification] › e2e\mode_parent\task-modification\penalty-edit-points.spec.ts:170:3 › Penalty edit points › Value above 10000 disables Save; exactly 10000 enables it
|
||||||
|
[1A[2K[125/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:94:3 › Reward edit cost › Input is pre-populated with the current cost
|
||||||
|
[1A[2K[126/287] [chromium-task-modification] › e2e\mode_parent\task-modification\pending-reset-on-edit.spec.ts:154:3 › Pending status resets on task/reward modification › 10.5 Editing task name only does NOT clear pending
|
||||||
|
[1A[2K[127/287] [chromium-task-modification] › e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:105:3 › Scroll to edited task after save › Edited chore card scrolls into viewport after override save
|
||||||
|
[1A[2K[128/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:104:3 › Reward edit cost › Cancel does not change the points needed display
|
||||||
|
[1A[2K[129/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:119:3 › Reward edit cost › Saving a new cost updates the points needed and shows ⭐ badge
|
||||||
|
[1A[2K[130/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\child-card-states.spec.ts:68:3 › Child Mode — Chore Visibility and Schedule States › 6.1 Chore with no schedule — visible normally in child mode
|
||||||
|
[1A[2K[131/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:134:3 › Reward edit cost › Setting cost to 0 makes the reward immediately redeemable
|
||||||
|
[1A[2K[132/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\extend-time.spec.ts:49:3 › Extend Time › 8.1 "Extend Time" appears in kebab menu only when chore is expired
|
||||||
|
[1A[2K[133/287] [chromium-task-modification] › e2e\mode_parent\task-modification\pending-reset-on-edit.spec.ts:180:3 › Pending status resets on task/reward modification › 10.4 Completed chores remain completed after point edit
|
||||||
|
[1A[2K[134/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\interval-anchor.spec.ts:36:3 › Schedule Interval Mode — Anchor Date Logic › 9.1 Interval every 1 day starting today — chore active today
|
||||||
|
[1A[2K[135/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:147:3 › Reward edit cost › Negative value disables the Save button
|
||||||
|
[1A[2K[136/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:158:3 › Reward edit cost › Value above 10000 disables Save; exactly 10000 enables it
|
||||||
|
[1A[2K[137/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:171:3 › Reward edit cost › Editing a pending reward shows a warning dialog — Cancel aborts the edit
|
||||||
|
[1A[2K[138/287] [chromium-task-modification] › e2e\mode_parent\task-modification\pending-reset-on-edit.spec.ts:201:3 › Pending status resets on task/reward modification › 10.3 Editing reward cost clears pending reward request
|
||||||
|
[1A[2K[139/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\interval-anchor.spec.ts:56:3 › Schedule Interval Mode — Anchor Date Logic › 9.2 Interval every 2 days starting yesterday — chore NOT active today
|
||||||
|
[1A[2K[140/287] [chromium-task-modification] › e2e\mode_parent\task-modification\reward-edit-cost.spec.ts:198:3 › Reward edit cost › Editing a pending reward — confirming the warning opens the override modal
|
||||||
|
[1A[2K[141/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\extend-time.spec.ts:61:3 › Extend Time › 8.2 "Extend Time" is absent when chore is not expired
|
||||||
|
[1A[2K[142/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\interval-anchor.spec.ts:83:3 › Schedule Interval Mode — Anchor Date Logic › 9.3 Interval every 2 days starting today — active today
|
||||||
|
[1A[2K[143/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\extend-time.spec.ts:70:3 › Extend Time › 8.3 Clicking "Extend Time" removes the TOO LATE badge
|
||||||
|
[1A[2K[144/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\interval-anchor.spec.ts:101:3 › Schedule Interval Mode — Anchor Date Logic › 9.4 Interval "Anytime" deadline — no "Due by" label
|
||||||
|
[1A[2K[145/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\extend-time.spec.ts:84:3 › Extend Time › 8.4 Extended chore no longer shows "Due by" label
|
||||||
|
[1A[2K[146/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\child-card-states.spec.ts:75:3 › Child Mode — Chore Visibility and Schedule States › 6.2 Chore scheduled for today — visible with "Due by" label
|
||||||
|
[1A[2K[147/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\extend-time.spec.ts:94:3 › Extend Time › 8.5 "Extend Time" disappears from kebab after extension
|
||||||
|
[1A[2K[148/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\child-card-states.spec.ts:83:3 › Child Mode — Chore Visibility and Schedule States › 6.3 Chore not scheduled for today — HIDDEN in child mode
|
||||||
|
[1A[2K[149/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-card-states.spec.ts:66:3 › Parent Mode — Chore Card Schedule States › 5.1 Chore with no schedule — normal appearance, no annotations
|
||||||
|
[1A[2K[150/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-days-mode.spec.ts:40:3 › Schedule Modal — Specific Days mode › 2.1 Clicking day chips toggles them active/inactive
|
||||||
|
[1A[2K[151/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\extend-time.spec.ts:104:3 › Extend Time › 8.6 Extension effect visible in child mode
|
||||||
|
[1A[2K[152/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-vs-child.spec.ts:51:3 › Parent vs Child Mode Comparison › 7.1 Parent mode shows all chores including off-day ones
|
||||||
|
[1A[2K[153/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\child-card-states.spec.ts:96:3 › Child Mode — Chore Visibility and Schedule States › 6.4 Chore with expired deadline — grayed out with TOO LATE badge (not hidden)
|
||||||
|
[1A[2K[154/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\extend-time.spec.ts:114:3 › Extend Time › 8.6b Extending same chore twice via API returns 409
|
||||||
|
[1A[2K[155/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\child-card-states.spec.ts:108:3 › Child Mode — Chore Visibility and Schedule States › 6.5 Chore with "Anytime" — visible, no badge, no "Due by"
|
||||||
|
[1A[2K[156/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\child-card-states.spec.ts:117:3 › Child Mode — Chore Visibility and Schedule States › 6.6 No kebab menu or schedule controls in child mode
|
||||||
|
[1A[2K[157/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-enable-disable.spec.ts:41:3 › Schedule Enable/Disable Toggle › 4.1 Toggle row is visible in the schedule modal
|
||||||
|
[1A[2K[158/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-days-mode.spec.ts:54:3 › Schedule Modal — Specific Days mode › 2.2 Default deadline row appears when at least one day is selected
|
||||||
|
[1A[2K[159/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-vs-child.spec.ts:69:3 › Parent vs Child Mode Comparison › 7.2 Child mode hides off-day chores
|
||||||
|
[1A[2K[160/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-days-mode.spec.ts:67:3 › Schedule Modal — Specific Days mode › 2.3 Default deadline "Anytime" toggle hides and restores time picker
|
||||||
|
[1A[2K[161/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-interval-mode.spec.ts:42:3 › Schedule Modal — Every X Days mode › 3.1 Interval stepper increments and decrements within 1–7
|
||||||
|
[1A[2K[162/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-vs-child.spec.ts:81:3 › Parent vs Child Mode Comparison › 7.3 Adding a schedule via parent mode hides chore in child mode on wrong day
|
||||||
|
[1A[2K[163/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-enable-disable.spec.ts:55:3 › Schedule Enable/Disable Toggle › 4.2 Toggling OFF shows "Paused" label and dims the form body
|
||||||
|
[1A[2K[164/287] [chromium-task-modification] › e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:135:3 › Scroll to edited task after save › Edited reward card scrolls into viewport after override save
|
||||||
|
[1A[2K[165/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-days-mode.spec.ts:102:3 › Schedule Modal — Specific Days mode › 2.4 Exception time — "Set different time" creates per-day override
|
||||||
|
[1A[2K[166/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-vs-child.spec.ts:98:3 › Parent vs Child Mode Comparison › 7.4 Removing a schedule via parent mode shows chore in child mode again
|
||||||
|
[1A[2K[167/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-card-states.spec.ts:76:3 › Parent Mode — Chore Card Schedule States › 5.2 Chore scheduled for today — shows "Due by" label, not grayed out
|
||||||
|
[1A[2K[168/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-enable-disable.spec.ts:81:3 › Schedule Enable/Disable Toggle › 4.3 Toggling ON restores "Enabled" label and form body
|
||||||
|
[1A[2K[169/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-card-states.spec.ts:85:3 › Parent Mode — Chore Card Schedule States › 5.3 Chore not scheduled for today — grayed out in parent mode but visible
|
||||||
|
[1A[2K[170/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-days-mode.spec.ts:129:3 › Schedule Modal — Specific Days mode › 2.5 Exception time — "Reset to default" removes the override
|
||||||
|
[1A[2K[171/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-vs-child.spec.ts:112:3 › Parent vs Child Mode Comparison › 7.5 Paused schedule — chore visible in both modes
|
||||||
|
[1A[2K[172/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-card-states.spec.ts:100:3 › Parent Mode — Chore Card Schedule States › 5.4 Chore with expired deadline — grayed out with TOO LATE badge
|
||||||
|
[1A[2K[173/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-interval-mode.spec.ts:66:3 › Schedule Modal — Every X Days mode › 3.2 Anchor date field shows today's date by default
|
||||||
|
[1A[2K[174/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-card-states.spec.ts:110:3 › Parent Mode — Chore Card Schedule States › 5.5 Chore with "Anytime" deadline — no "Due by" label, no TOO LATE badge
|
||||||
|
[1A[2K[175/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-enable-disable.spec.ts:99:3 › Schedule Enable/Disable Toggle › 4.4 Save paused state — persists on reopen
|
||||||
|
[1A[2K[176/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-days-mode.spec.ts:150:3 › Schedule Modal — Specific Days mode › 2.6 Save with specific days — schedule persists on reopen
|
||||||
|
[1A[2K[177/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-card-states.spec.ts:122:3 › Parent Mode — Chore Card Schedule States › 5.6 Kebab menu always accessible on grayed-out chores
|
||||||
|
[1A[2K[178/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-interval-mode.spec.ts:76:3 › Schedule Modal — Every X Days mode › 3.3 Next occurrence preview label updates correctly
|
||||||
|
[1A[2K[179/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-load.spec.ts:40:3 › Schedule Loading — Backward Compatibility › 11.1 Schedule without "enabled" field defaults to enabled in modal
|
||||||
|
[1A[2K[180/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\parent-card-states.spec.ts:139:3 › Parent Mode — Chore Card Schedule States › 5.7 Expired chore cannot be triggered by tapping — no confirm dialog
|
||||||
|
[1A[2K[181/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-days-mode.spec.ts:183:3 › Schedule Modal — Specific Days mode › 2.7 Save with zero days deletes the schedule
|
||||||
|
[1A[2K[182/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-enable-disable.spec.ts:122:3 › Schedule Enable/Disable Toggle › 4.5 Dirty detection — toggling enabled state enables/disables Save
|
||||||
|
[1A[2K[183/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-interval-mode.spec.ts:96:3 › Schedule Modal — Every X Days mode › 3.4 Interval deadline "Anytime" toggle works
|
||||||
|
[1A[2K 1) [chromium-task-modification] › e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:135:3 › Scroll to edited task after save › Edited reward card scrolls into viewport after override save
|
||||||
|
|
||||||
|
Error: [2mexpect([22m[31mreceived[39m[2m).[22mtoBe[2m([22m[32mexpected[39m[2m) // Object.is equality[22m
|
||||||
|
|
||||||
|
Expected: [32mtrue[39m
|
||||||
|
Received: [31mfalse[39m
|
||||||
|
|
||||||
|
159 | // After SSE override event → list refreshes → card scrolls into view
|
||||||
|
160 | await expect(targetCard).toBeVisible({ timeout: 5000 })
|
||||||
|
> 161 | expect(await isInViewport(targetCard)).toBe(true)
|
||||||
|
| ^
|
||||||
|
162 | })
|
||||||
|
163 | })
|
||||||
|
164 |
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:161:44
|
||||||
|
|
||||||
|
Error Context: test-results\mode_parent-task-modificat-7e620-iewport-after-override-save-chromium-task-modification\error-context.md
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[184/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-interval-mode.spec.ts:122:3 › Schedule Modal — Every X Days mode › 3.5 Save interval schedule — persists on reopen
|
||||||
|
[1A[2K[185/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-enable-disable.spec.ts:145:3 › Schedule Enable/Disable Toggle › 4.6 Paused schedule in parent mode — chore is visible and not grayed out
|
||||||
|
[1A[2K[186/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-load.spec.ts:66:3 › Schedule Loading — Backward Compatibility › 11.2 Schedule with interval_has_deadline=false shows "Anytime" in modal
|
||||||
|
[1A[2K[187/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-sse.spec.ts:40:3 › SSE Real-Time Updates › 10.1 Setting a schedule via API reflects in an already-open parent view
|
||||||
|
[1A[2K[188/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-days-mode.spec.ts:212:3 › Schedule Modal — Specific Days mode › 2.8 Dirty detection — changing days enables Save
|
||||||
|
[1A[2K[189/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-enable-disable.spec.ts:162:3 › Schedule Enable/Disable Toggle › 4.7 Paused schedule in child mode — chore is visible
|
||||||
|
[1A[2K[190/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-modal.spec.ts:38:3 › Schedule Modal — Opening & Structure › 1.1 Schedule option appears in kebab menu
|
||||||
|
[1A[2K[191/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-load.spec.ts:89:3 › Schedule Loading — Backward Compatibility › 11.3 Days schedule with per-day exception loads deadline rows correctly
|
||||||
|
[1A[2K[192/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-interval-mode.spec.ts:158:3 › Schedule Modal — Every X Days mode › 3.6 Dirty detection — changing interval enables Save
|
||||||
|
[1A[2K[193/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-enable-disable.spec.ts:172:3 › Schedule Enable/Disable Toggle › 4.8 Paused schedule shows no "Due by" label
|
||||||
|
[1A[2K[194/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-load.spec.ts:145:3 › Schedule Loading — Backward Compatibility › 11.4 Interval schedule restores anchor date and interval on reopen
|
||||||
|
[1A[2K[195/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-sse.spec.ts:58:3 › SSE Real-Time Updates › 10.2 Deleting a schedule via API un-grays the chore in parent view
|
||||||
|
[1A[2K[196/287] [chromium-profile-button] › e2e\mode_parent\profile-button\profile-button-temporary.spec.ts:22:3 › Parent profile button – temporary parent mode › Badge – no 🔒 badge in temporary parent mode
|
||||||
|
[1A[2K[197/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-modal.spec.ts:46:3 › Schedule Modal — Opening & Structure › 1.2 Schedule modal opens with correct title and subtitle
|
||||||
|
[1A[2K[198/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-sse.spec.ts:77:3 › SSE Real-Time Updates › 10.3 Extending time via API reflects in an already-open parent view
|
||||||
|
[1A[2K[199/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-modal.spec.ts:55:3 › Schedule Modal — Opening & Structure › 1.3 Default state: Specific Days mode, no chips selected, toggle Enabled
|
||||||
|
[1A[2K[200/287] [chromium-profile-button] › e2e\mode_parent\profile-button\profile-button.spec.ts:29:3 › Parent profile button – permanent parent mode › Menu – Child Mode exits parent mode
|
||||||
|
[1A[2K[201/287] [chromium-profile-button] › e2e\mode_parent\profile-button\profile-button.spec.ts:9:3 › Parent profile button – permanent parent mode › Badge – shows 🔒 in permanent parent mode
|
||||||
|
[1A[2K[202/287] [chromium-profile-button] › e2e\mode_parent\profile-button\profile-button.spec.ts:14:3 › Parent profile button – permanent parent mode › Menu – shows user name and email
|
||||||
|
[1A[2K[203/287] [chromium-profile-button] › e2e\mode_parent\profile-button\profile-button.spec.ts:39:3 › Parent profile button – permanent parent mode › Menu – Child Mode re-entry without checkbox enters temporary mode
|
||||||
|
[1A[2K[204/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-modal.spec.ts:78:3 › Schedule Modal — Opening & Structure › 1.4 Mode toggle switches between Specific Days and Every X Days
|
||||||
|
[1A[2K[205/287] [chromium-profile-button] › e2e\mode_parent\profile-button\profile-button.spec.ts:58:3 › Parent profile button – permanent parent mode › Menu – Child Mode re-entry with checkbox enters permanent mode
|
||||||
|
[1A[2K[206/287] [chromium-profile-button] › e2e\mode_parent\profile-button\profile-button.spec.ts:21:3 › Parent profile button – permanent parent mode › Menu – Profile option navigates to the User Profile page
|
||||||
|
[1A[2K[207/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-modal.spec.ts:95:3 › Schedule Modal — Opening & Structure › 1.5 Cancel closes the modal without saving
|
||||||
|
[1A[2K[208/287] [chromium-profile-button] › e2e\mode_parent\profile-button\profile-button.spec.ts:77:3 › Parent profile button – permanent parent mode › Menu – Sign Out navigates to landing page
|
||||||
|
[1A[2K[209/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:48:3 › User Profile – editing › Profile page loads with correct data
|
||||||
|
[1A[2K[210/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:26:3 › User Profile – Change Parent PIN › Change Parent PIN link navigates to PIN setup page
|
||||||
|
[1A[2K[211/287] [chromium-chore-scheduler] › e2e\mode_parent\chore-scheduler\schedule-modal.spec.ts:120:3 › Schedule Modal — Opening & Structure › 1.6 Save is disabled when form is not dirty
|
||||||
|
[1A[2K[212/287] [chromium-tasks-rewards] › e2e\example.spec.ts:3:1 › has title
|
||||||
|
[1A[2K[213/287] [chromium-tasks-rewards] › e2e\example.spec.ts:10:1 › get started link
|
||||||
|
[1A[2K[214/287] [chromium-tasks-rewards] › e2e\mode_parent\notifications\notification-card-overflow.spec.ts:76:3 › Notification card overflow › Long notification entity name wraps or clips within card
|
||||||
|
[1A[2K[215/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:35:3 › User Profile – Change Parent PIN › Back from PIN setup page returns to profile
|
||||||
|
[1A[2K[216/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:58:3 › User Profile – editing › Back button navigates back
|
||||||
|
[1A[2K[217/287] [chromium-tasks-rewards] › e2e\mode_parent\notifications\notification-click-scroll.spec.ts:125:3 › Notification click navigates and scrolls to task › Clicking a chore notification navigates and scrolls to the chore card
|
||||||
|
[1A[2K[218/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:46:3 › User Profile – Change Parent PIN › Send Verification Code advances to step 2
|
||||||
|
[1A[2K[219/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:70:3 › User Profile – editing › Save is disabled when form is clean (not dirty)
|
||||||
|
[1A[2K[220/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:60:3 › User Profile – Change Parent PIN › Resend Code button appears after delay
|
||||||
|
[1A[2K[221/287] [chromium-tasks-rewards] › e2e\mode_parent\notifications\notification-card-overflow.spec.ts:56:3 › Notification card overflow › Notification text does not overflow card boundaries
|
||||||
|
[1A[2K[222/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:76:3 › User Profile – editing › Save is disabled when First Name is empty
|
||||||
|
[1A[2K[223/287] [chromium-tasks-rewards] › e2e\mode_parent\rewards\reward-cancel.spec.ts:13:3 › Reward cancellation › Cancel reward creation
|
||||||
|
[1A[2K[224/287] [chromium-tasks-rewards] › e2e\mode_parent\rewards\reward-cancel.spec.ts:39:3 › Reward cancellation › Cancel reward edit
|
||||||
|
[1A[2K[225/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:85:3 › User Profile – editing › Save is disabled when Last Name is empty
|
||||||
|
[1A[2K[226/287] [chromium-tasks-rewards] › e2e\mode_parent\rewards\reward-convert-default.spec.ts:13:3 › Convert default reward › Convert a default reward to a user item
|
||||||
|
[1A[2K[227/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:94:3 › User Profile – editing › Save is disabled when both name fields are empty
|
||||||
|
[1A[2K[228/287] [chromium-tasks-rewards] › e2e\mode_parent\rewards\reward-create-edit.spec.ts:13:3 › Reward management › Create a new reward
|
||||||
|
[1A[2K[229/287] [chromium-tasks-rewards] › e2e\mode_parent\rewards\reward-delete-default.spec.ts:13:3 › Default reward cost edit and restore › Edit default cost and verify restoration on delete
|
||||||
|
[1A[2K[230/287] [chromium-tasks-rewards] › e2e\mode_parent\task-sorting\child-sort-order.spec.ts:179:3 › Child mode sort order › Child chore sort: scheduled today (earliest deadline) → general → pending
|
||||||
|
[1A[2K[231/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:103:3 › User Profile – editing › Save enables when a name is changed
|
||||||
|
[1A[2K[232/287] [chromium-tasks-rewards] › e2e\mode_parent\rewards\reward-create-edit.spec.ts:50:3 › Reward management › Edit an existing reward
|
||||||
|
[1A[2K[233/287] [chromium-tasks-rewards] › e2e\mode_parent\task-sorting\parent-sort-order.spec.ts:182:3 › Parent mode sort order › Parent chore sort: pending → general → expired → completed → unscheduled
|
||||||
|
[1A[2K[234/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:111:3 › User Profile – editing › Save persists name changes and shows confirmation modal
|
||||||
|
[1A[2K[235/287] [chromium-tasks-rewards] › e2e\mode_parent\rewards\reward-create-edit.spec.ts:107:3 › Reward management › Delete a reward
|
||||||
|
[1A[2K[236/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-cancel.spec.ts:11:3 › Chore creation/edit cancellation › Cancel chore creation
|
||||||
|
[1A[2K[237/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:70:3 › User Profile – Change Parent PIN › Invalid code shows error
|
||||||
|
[1A[2K[238/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:129:3 › User Profile – editing › Cancel discards unsaved changes
|
||||||
|
[1A[2K[239/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:81:3 › User Profile – Change Parent PIN › Valid code advances to step 3 (Create PIN)
|
||||||
|
[1A[2K[240/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:141:3 › User Profile – editing › Email field is read-only
|
||||||
|
[1A[2K[241/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:99:3 › User Profile – Change Parent PIN › Set PIN disabled when PINs do not match
|
||||||
|
[1A[2K[242/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:149:3 › User Profile – editing › Change profile image (built-in)
|
||||||
|
[1A[2K[243/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:116:3 › User Profile – Change Parent PIN › Set PIN disabled for fewer than 4 digits
|
||||||
|
[1A[2K[244/287] [chromium-tasks-rewards] › e2e\mode_parent\notifications\notification-click-scroll.spec.ts:149:3 › Notification click navigates and scrolls to task › Clicking a reward notification scrolls reward card into viewport
|
||||||
|
[1A[2K[245/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:133:3 › User Profile – Change Parent PIN › Set PIN succeeds with matching 4-digit PIN – shows success step
|
||||||
|
[1A[2K[246/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:193:3 › User Profile – editing › Upload a custom profile image
|
||||||
|
[1A[2K 2) [chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-cancel.spec.ts:11:3 › Chore creation/edit cancellation › Cancel chore creation
|
||||||
|
|
||||||
|
Error: [2mexpect([22m[31mlocator[39m[2m).[22mtoHaveCount[2m([22m[32mexpected[39m[2m)[22m failed
|
||||||
|
|
||||||
|
Locator: locator('text=Test')
|
||||||
|
Expected: [32m0[39m
|
||||||
|
Received: [31m1[39m
|
||||||
|
Timeout: 5000ms
|
||||||
|
|
||||||
|
Call log:
|
||||||
|
[2m - Expect "toHaveCount" with timeout 5000ms[22m
|
||||||
|
[2m - waiting for locator('text=Test')[22m
|
||||||
|
[2m 9 × locator resolved to 1 element[22m
|
||||||
|
[2m - unexpected value "1"[22m
|
||||||
|
|
||||||
|
|
||||||
|
33 | await expect(page.getByText('Clean your mess', { exact: true })).toBeVisible()
|
||||||
|
34 | // expect: No chore named 'Test' exists
|
||||||
|
> 35 | await expect(page.locator('text=Test')).toHaveCount(0)
|
||||||
|
| ^
|
||||||
|
36 | })
|
||||||
|
37 |
|
||||||
|
38 | test('Cancel chore edit', async ({ page }) => {
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\tasks\chore-cancel.spec.ts:35:45
|
||||||
|
|
||||||
|
Error Context: test-results\mode_parent-tasks-chore-ca-9c64e-ation-Cancel-chore-creation-chromium-tasks-rewards\error-context.md
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[247/287] [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:156:3 › User Profile – Change Parent PIN › Back from success step exits parent mode and goes to child selector
|
||||||
|
[1A[2K[248/287] [chromium-tasks-rewards] › e2e\mode_parent\task-sorting\child-sort-order.spec.ts:214:3 › Child mode sort order › Child reward sort: pending first, then ascending by points needed
|
||||||
|
[1A[2K[249/287] [chromium-user-profile] › e2e\mode_parent\user-profile\profile-editing.spec.ts:216:3 › User Profile – editing › Change Password shows email-sent modal
|
||||||
|
[1A[2K[250/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-cancel.spec.ts:38:3 › Chore creation/edit cancellation › Cancel chore edit
|
||||||
|
[1A[2K[251/287] [chromium-tasks-rewards] › e2e\mode_parent\task-sorting\parent-sort-order.spec.ts:210:3 › Parent mode sort order › Parent reward sort: pending requests appear first
|
||||||
|
[1A[2K 3) [chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:156:3 › User Profile – Change Parent PIN › Back from success step exits parent mode and goes to child selector
|
||||||
|
|
||||||
|
Error: apiRequestContext.post: connect ECONNREFUSED ::1:5000
|
||||||
|
Call log:
|
||||||
|
[2m - → POST http://localhost:5000/user/set-pin[22m
|
||||||
|
[2m - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.7632.6 Safari/537.36[22m
|
||||||
|
[2m - accept: */*[22m
|
||||||
|
[2m - accept-encoding: gzip,deflate,br[22m
|
||||||
|
[2m - content-type: application/json[22m
|
||||||
|
[2m - content-length: 14[22m
|
||||||
|
[2m - cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiJlZTk5MGEzOC1kYmVkLTRlNzEtYTQzYi0yOTQxNWI0OWE1MWYiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzU5Njc2NjN9.SdtrcRmhNT7O4Zqv2h94RJdsPuFe5haX0yYu2RRkrSg[22m
|
||||||
|
|
||||||
|
at apiRequestContext.post: connect ECONNREFUSED ::1:5000
|
||||||
|
at setPinDirectly (D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\user-profile\change-pin.spec.ts:15:17)
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\user-profile\change-pin.spec.ts:23:5
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K 4) [chromium-tasks-rewards] › e2e\mode_parent\task-sorting\child-sort-order.spec.ts:214:3 › Child mode sort order › Child reward sort: pending first, then ascending by points needed
|
||||||
|
|
||||||
|
Error: apiRequestContext.delete: read ECONNRESET
|
||||||
|
Call log:
|
||||||
|
[2m - → DELETE https://localhost:5173/api/task/2519b537-1af6-44c4-b6c4-cde6d7bde1db[22m
|
||||||
|
[2m - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.7632.6 Safari/537.36[22m
|
||||||
|
[2m - accept: */*[22m
|
||||||
|
[2m - accept-encoding: gzip,deflate,br[22m
|
||||||
|
[2m - cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiJlZTk5MGEzOC1kYmVkLTRlNzEtYTQzYi0yOTQxNWI0OWE1MWYiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzU5Njc2NjN9.SdtrcRmhNT7O4Zqv2h94RJdsPuFe5haX0yYu2RRkrSg[22m
|
||||||
|
|
||||||
|
|
||||||
|
170 | if (childId) await request.delete(`/api/child/${childId}`)
|
||||||
|
171 | for (const id of [schedEarlyId, schedLateId, generalChoreId, pendingChoreId, nonTodayChoreId]) {
|
||||||
|
> 172 | if (id) await request.delete(`/api/task/${id}`)
|
||||||
|
| ^
|
||||||
|
173 | }
|
||||||
|
174 | for (const id of [pendingRewardId, cheapRewardId, expensiveRewardId]) {
|
||||||
|
175 | if (id) await request.delete(`/api/reward/${id}`)
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\task-sorting\child-sort-order.spec.ts:172:35
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K 5) [chromium-tasks-rewards] › e2e\mode_parent\notifications\notification-click-scroll.spec.ts:149:3 › Notification click navigates and scrolls to task › Clicking a reward notification scrolls reward card into viewport
|
||||||
|
|
||||||
|
Error: apiRequestContext.delete: read ECONNRESET
|
||||||
|
Call log:
|
||||||
|
[2m - → DELETE https://localhost:5173/api/task/502b34cf-b21f-4510-b0bb-cd2fc871db04[22m
|
||||||
|
[2m - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.7632.6 Safari/537.36[22m
|
||||||
|
[2m - accept: */*[22m
|
||||||
|
[2m - accept-encoding: gzip,deflate,br[22m
|
||||||
|
[2m - cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiJlZTk5MGEzOC1kYmVkLTRlNzEtYTQzYi0yOTQxNWI0OWE1MWYiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzU5Njc2NjN9.SdtrcRmhNT7O4Zqv2h94RJdsPuFe5haX0yYu2RRkrSg[22m
|
||||||
|
|
||||||
|
|
||||||
|
119 | test.afterAll(async ({ request }) => {
|
||||||
|
120 | if (childId) await request.delete(`/api/child/${childId}`)
|
||||||
|
> 121 | for (const id of choreIds) await request.delete(`/api/task/${id}`)
|
||||||
|
| ^
|
||||||
|
122 | if (rewardId) await request.delete(`/api/reward/${rewardId}`)
|
||||||
|
123 | })
|
||||||
|
124 |
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\notifications\notification-click-scroll.spec.ts:121:52
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[252/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-create-edit.spec.ts:12:3 › Chore management › Create a new chore (parent mode)
|
||||||
|
[1A[2K[253/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-cancel.spec.ts:11:3 › Kindness act cancellation › Cancel kindness act creation
|
||||||
|
[1A[2K[254/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-cancel.spec.ts:31:3 › Kindness act cancellation › Cancel kindness act edit
|
||||||
|
[1A[2K[255/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-create-edit.spec.ts:13:3 › Kindness act management › Create a new kindness act (parent mode)
|
||||||
|
[1A[2K 6) [chromium-tasks-rewards] › e2e\mode_parent\task-sorting\parent-sort-order.spec.ts:210:3 › Parent mode sort order › Parent reward sort: pending requests appear first
|
||||||
|
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at https://localhost:5173/parent/213673b4-d3f7-4dca-9c4d-dab039195cf1
|
||||||
|
Call log:
|
||||||
|
[2m - navigating to "https://localhost:5173/parent/213673b4-d3f7-4dca-9c4d-dab039195cf1", waiting until "load"[22m
|
||||||
|
|
||||||
|
|
||||||
|
209 |
|
||||||
|
210 | test('Parent reward sort: pending requests appear first', async ({ page }) => {
|
||||||
|
> 211 | await page.goto(`/parent/${childId}`)
|
||||||
|
| ^
|
||||||
|
212 | const section = rewardSection(page)
|
||||||
|
213 | await section.locator('.item-card').first().waitFor({ state: 'visible' })
|
||||||
|
214 |
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\task-sorting\parent-sort-order.spec.ts:211:16
|
||||||
|
|
||||||
|
Error: apiRequestContext.delete: connect ECONNREFUSED ::1:5173
|
||||||
|
Call log:
|
||||||
|
[2m - → DELETE https://localhost:5173/api/child/213673b4-d3f7-4dca-9c4d-dab039195cf1[22m
|
||||||
|
[2m - user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.7632.6 Safari/537.36[22m
|
||||||
|
[2m - accept: */*[22m
|
||||||
|
[2m - accept-encoding: gzip,deflate,br[22m
|
||||||
|
[2m - cookie: access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiJlZTk5MGEzOC1kYmVkLTRlNzEtYTQzYi0yOTQxNWI0OWE1MWYiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzU5Njc2NjN9.SdtrcRmhNT7O4Zqv2h94RJdsPuFe5haX0yYu2RRkrSg[22m
|
||||||
|
|
||||||
|
at apiRequestContext.delete: connect ECONNREFUSED ::1:5173
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\task-sorting\parent-sort-order.spec.ts:167:38
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[256/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-cancel.spec.ts:11:3 › Penalty creation/edit cancellation › Cancel penalty creation
|
||||||
|
[1A[2K 7) [chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-create-edit.spec.ts:12:3 › Chore management › Create a new chore (parent mode)
|
||||||
|
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at https://localhost:5173/parent/tasks/chores
|
||||||
|
Call log:
|
||||||
|
[2m - navigating to "https://localhost:5173/parent/tasks/chores", waiting until "load"[22m
|
||||||
|
|
||||||
|
|
||||||
|
14 | const name = `Wash dishes ${suffix}`
|
||||||
|
15 | // 1. Navigate to /parent/tasks/chores
|
||||||
|
> 16 | await page.goto('/parent/tasks/chores')
|
||||||
|
| ^
|
||||||
|
17 | // expect: The parent dashboard loads and shows the chores list
|
||||||
|
18 | await expect(page.getByText('Clean your mess', { exact: true })).toBeVisible()
|
||||||
|
19 |
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\tasks\chore-create-edit.spec.ts:16:16
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[257/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-create-edit.spec.ts:51:3 › Chore management › Edit an existing chore
|
||||||
|
[1A[2K[258/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-create-edit.spec.ts:107:3 › Chore management › Delete a chore
|
||||||
|
[1A[2K 8) [chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-cancel.spec.ts:11:3 › Kindness act cancellation › Cancel kindness act creation
|
||||||
|
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at https://localhost:5173/parent/tasks/chores
|
||||||
|
Call log:
|
||||||
|
[2m - navigating to "https://localhost:5173/parent/tasks/chores", waiting until "load"[22m
|
||||||
|
|
||||||
|
|
||||||
|
10 |
|
||||||
|
11 | test('Cancel kindness act creation', async ({ page }) => {
|
||||||
|
> 12 | await page.goto('/parent/tasks/chores')
|
||||||
|
| ^
|
||||||
|
13 | await page.click('text=Kindness Acts')
|
||||||
|
14 | await page.getByRole('button', { name: 'Create Kindness Act' }).click()
|
||||||
|
15 | await page.evaluate(() => {
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\tasks\kindness-cancel.spec.ts:12:16
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K 9) [chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-cancel.spec.ts:31:3 › Kindness act cancellation › Cancel kindness act edit
|
||||||
|
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at https://localhost:5173/parent/tasks/chores
|
||||||
|
Call log:
|
||||||
|
[2m - navigating to "https://localhost:5173/parent/tasks/chores", waiting until "load"[22m
|
||||||
|
|
||||||
|
|
||||||
|
30 |
|
||||||
|
31 | test('Cancel kindness act edit', async ({ page }) => {
|
||||||
|
> 32 | await page.goto('/parent/tasks/chores')
|
||||||
|
| ^
|
||||||
|
33 | await page.click('text=Kindness Acts')
|
||||||
|
34 | if (!(await page.getByText('Share toys', { exact: true }).count())) {
|
||||||
|
35 | await page.getByRole('button', { name: 'Create Kindness Act' }).click()
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\tasks\kindness-cancel.spec.ts:32:16
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K 10) [chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-create-edit.spec.ts:13:3 › Kindness act management › Create a new kindness act (parent mode)
|
||||||
|
|
||||||
|
Error: page.goto: net::ERR_CONNECTION_REFUSED at https://localhost:5173/parent/tasks/chores
|
||||||
|
Call log:
|
||||||
|
[2m - navigating to "https://localhost:5173/parent/tasks/chores", waiting until "load"[22m
|
||||||
|
|
||||||
|
|
||||||
|
17 |
|
||||||
|
18 | // 1. navigate once and switch to kindness tab
|
||||||
|
> 19 | await page.goto('/parent/tasks/chores')
|
||||||
|
| ^
|
||||||
|
20 | await page.click('text=Kindness Acts')
|
||||||
|
21 | await expect(page.locator('text=Kindness Acts')).toBeVisible()
|
||||||
|
22 |
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\tasks\kindness-create-edit.spec.ts:19:16
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[259/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-create-edit.spec.ts:48:3 › Kindness act management › Edit an existing kindness act
|
||||||
|
[1A[2K[260/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-create-edit.spec.ts:98:3 › Kindness act management › Delete a kindness act
|
||||||
|
[1A[2K[261/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-cancel.spec.ts:32:3 › Penalty creation/edit cancellation › Cancel penalty edit
|
||||||
|
[1A[2K[262/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-create-edit.spec.ts:13:3 › Penalty management › Create a new penalty (parent mode)
|
||||||
|
[1A[2K[263/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\test-simple-chore.spec.ts:3:1 › Simple chore creation test
|
||||||
|
[1A[2K[264/287] [chromium-no-pin] › e2e\example.spec.ts:3:1 › has title
|
||||||
|
[1A[2K[265/287] [chromium-no-pin] › e2e\example.spec.ts:10:1 › get started link
|
||||||
|
[1A[2K[266/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-create-edit.spec.ts:37:3 › Penalty management › Edit an existing penalty
|
||||||
|
[1A[2K 11) [chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-cancel.spec.ts:32:3 › Penalty creation/edit cancellation › Cancel penalty edit
|
||||||
|
|
||||||
|
Error: [2mexpect([22m[31mlocator[39m[2m).[22mtoBeVisible[2m([22m[2m)[22m failed
|
||||||
|
|
||||||
|
Locator: getByText('No screen time', { exact: true })
|
||||||
|
Expected: visible
|
||||||
|
Error: strict mode violation: getByText('No screen time', { exact: true }) resolved to 2 elements:
|
||||||
|
1) <span class="name" data-v-392076e6="">No screen time</span> aka getByText('No screen time').first()
|
||||||
|
2) <span class="name" data-v-392076e6="">No screen time</span> aka getByText('No screen time').nth(1)
|
||||||
|
|
||||||
|
Call log:
|
||||||
|
[2m - Expect "toBeVisible" with timeout 5000ms[22m
|
||||||
|
[2m - waiting for getByText('No screen time', { exact: true })[22m
|
||||||
|
|
||||||
|
|
||||||
|
38 | await page.locator('#points').fill('10')
|
||||||
|
39 | await page.getByRole('button', { name: 'Create' }).click()
|
||||||
|
> 40 | await expect(page.getByText('No screen time', { exact: true })).toBeVisible()
|
||||||
|
| ^
|
||||||
|
41 | }
|
||||||
|
42 | // open edit by clicking row
|
||||||
|
43 | await page.getByText('No screen time', { exact: true }).click()
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\tasks\penalty-cancel.spec.ts:40:71
|
||||||
|
|
||||||
|
Error Context: test-results\mode_parent-tasks-penalty--56070-llation-Cancel-penalty-edit-chromium-tasks-rewards\error-context.md
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[267/287] [chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-create-edit.spec.ts:84:3 › Penalty management › Delete a penalty
|
||||||
|
[1A[2K 12) [chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-cancel.spec.ts:38:3 › Chore creation/edit cancellation › Cancel chore edit
|
||||||
|
|
||||||
|
[31mTest timeout of 30000ms exceeded.[39m
|
||||||
|
|
||||||
|
Error: locator.click: Test timeout of 30000ms exceeded.
|
||||||
|
Call log:
|
||||||
|
[2m - waiting for getByText('Wash dishes', { exact: true })[22m
|
||||||
|
|
||||||
|
|
||||||
|
56 | await page.getByRole('button', { name: 'Create' }).click()
|
||||||
|
57 | }
|
||||||
|
> 58 | await page.getByText('Wash dishes', { exact: true }).click()
|
||||||
|
| ^
|
||||||
|
59 |
|
||||||
|
60 | // 2. Modify the name then cancel
|
||||||
|
61 | await page.evaluate(() => {
|
||||||
|
at D:\Python Utilities\Reward\frontend\vue-app\e2e\mode_parent\tasks\chore-cancel.spec.ts:58:58
|
||||||
|
|
||||||
|
Error Context: test-results\mode_parent-tasks-chore-ca-ef6a3-cellation-Cancel-chore-edit-chromium-tasks-rewards\error-context.md
|
||||||
|
|
||||||
|
|
||||||
|
[1A[2K[268/287] [chromium-create-child] › e2e\mode_parent\create-child\authorization.spec.ts:9:3 › Create Child › Add Child FAB is hidden when parent auth is expired
|
||||||
|
[1A[2K[269/287] [chromium-create-child] › e2e\mode_parent\create-child\validation.spec.ts:15:3 › Create Child › Reject submission when Name is empty
|
||||||
|
[1A[2K[270/287] [chromium-create-child] › e2e\mode_parent\create-child\navigation.spec.ts:6:3 › Create Child › Cancel navigates back without saving
|
||||||
|
[1A[2K[271/287] [chromium-create-child] › e2e\mode_parent\create-child\sse.spec.ts:9:3 › Create Child › New child appears in list without page reload
|
||||||
|
[1A[2K[272/287] [chromium-create-child] › e2e\mode_parent\create-child\happy-path.spec.ts:37:3 › Create Child › Create a child with name and age only
|
||||||
|
[1A[2K[273/287] [chromium-create-child] › e2e\mode_parent\create-child\validation.spec.ts:24:3 › Create Child › Reject submission when Name is whitespace only
|
||||||
|
[1A[2K[274/287] [chromium-create-child] › e2e\mode_parent\create-child\validation.spec.ts:34:3 › Create Child › Reject submission when Age is empty
|
||||||
|
[1A[2K[275/287] [chromium-create-child] › e2e\mode_parent\create-child\validation.spec.ts:43:3 › Create Child › Reject negative age
|
||||||
|
[1A[2K[276/287] [chromium-create-child] › e2e\mode_parent\create-child\validation.spec.ts:53:3 › Create Child › Enforce maximum Name length of 64 characters
|
||||||
|
[1A[2K[277/287] [chromium-create-child] › e2e\mode_parent\create-child\validation.spec.ts:80:3 › Create Child › Reject age greater than 120
|
||||||
|
[1A[2K[278/287] [chromium-create-child] › e2e\mode_parent\create-child\happy-path.spec.ts:80:3 › Create Child › Create a child via the inline Create button in empty state
|
||||||
|
[1A[2K[279/287] [chromium-create-child] › e2e\mode_parent\create-child\sse.spec.ts:52:3 › Create Child › New child appears in child mode list without page reload
|
||||||
|
[1A[2K[280/287] [chromium-user-profile-delete] › e2e\mode_parent\user-profile\delete-account.spec.ts:9:3 › User Profile – Delete Account › Delete My Account opens confirmation dialog
|
||||||
|
[1A[2K[281/287] [chromium-create-child] › e2e\mode_parent\create-child\happy-path.spec.ts:122:3 › Create Child › Create a child with a custom uploaded image
|
||||||
|
[1A[2K[282/287] [chromium-user-profile-delete] › e2e\mode_parent\user-profile\delete-account.spec.ts:25:3 › User Profile – Delete Account › Delete button stays disabled for incomplete email
|
||||||
|
[1A[2K[283/287] [chromium-user-profile-delete] › e2e\mode_parent\user-profile\delete-account.spec.ts:37:3 › User Profile – Delete Account › Delete button enables when matching email is entered
|
||||||
|
[1A[2K[284/287] [chromium-user-profile-delete] › e2e\mode_parent\user-profile\delete-account.spec.ts:49:3 › User Profile – Delete Account › Cancel closes the dialog without deleting the account
|
||||||
|
[1A[2K[285/287] [chromium-user-profile-delete] › e2e\mode_parent\user-profile\delete-account.spec.ts:64:3 › User Profile – Delete Account › Backdrop click does NOT close the delete dialog
|
||||||
|
[1A[2K[286/287] [chromium-user-profile-delete] › e2e\mode_parent\user-profile\delete-account.spec.ts:76:3 › User Profile – Delete Account › Successful deletion: confirmation modal appears then redirects to landing page
|
||||||
|
[1A[2K[287/287] [chromium-user-profile-delete] › e2e\mode_parent\user-profile\delete-account.spec.ts:97:3 › User Profile – Delete Account › Login after deletion is blocked with an appropriate error
|
||||||
|
[1A[2K 12 failed
|
||||||
|
[chromium-task-modification] › e2e\mode_parent\task-modification\scroll-to-edited-task.spec.ts:135:3 › Scroll to edited task after save › Edited reward card scrolls into viewport after override save
|
||||||
|
[chromium-user-profile-pin] › e2e\mode_parent\user-profile\change-pin.spec.ts:156:3 › User Profile – Change Parent PIN › Back from success step exits parent mode and goes to child selector
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\notifications\notification-click-scroll.spec.ts:149:3 › Notification click navigates and scrolls to task › Clicking a reward notification scrolls reward card into viewport
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\task-sorting\child-sort-order.spec.ts:214:3 › Child mode sort order › Child reward sort: pending first, then ascending by points needed
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\task-sorting\parent-sort-order.spec.ts:210:3 › Parent mode sort order › Parent reward sort: pending requests appear first
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-cancel.spec.ts:11:3 › Chore creation/edit cancellation › Cancel chore creation
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-cancel.spec.ts:38:3 › Chore creation/edit cancellation › Cancel chore edit
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\tasks\chore-create-edit.spec.ts:12:3 › Chore management › Create a new chore (parent mode)
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-cancel.spec.ts:11:3 › Kindness act cancellation › Cancel kindness act creation
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-cancel.spec.ts:31:3 › Kindness act cancellation › Cancel kindness act edit
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\tasks\kindness-create-edit.spec.ts:13:3 › Kindness act management › Create a new kindness act (parent mode)
|
||||||
|
[chromium-tasks-rewards] › e2e\mode_parent\tasks\penalty-cancel.spec.ts:32:3 › Penalty creation/edit cancellation › Cancel penalty edit
|
||||||
|
1 skipped
|
||||||
|
4 did not run
|
||||||
|
270 passed (3.0m)
|
||||||
4
test-results/.last-run.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"status": "failed",
|
||||||
|
"failedTests": []
|
||||||
|
}
|
||||||