Skip to content

Release v5.4.4#234

Merged
parhumm merged 23 commits intomasterfrom
development
Mar 17, 2026
Merged

Release v5.4.4#234
parhumm merged 23 commits intomasterfrom
development

Conversation

@parhumm
Copy link
Copy Markdown
Contributor

@parhumm parhumm commented Mar 17, 2026

Changelog

Fixed

  • Chart data not showing due to incorrect bounds check (PR #232)

Improved

  • Added cpt: prefix guidance to content type exclusion setting

Summary by CodeRabbit

  • Release

    • v5.4.4 released (2026-03-17); changelog and plugin version updated.
  • Bug Fixes

    • Fixed two chart data issues: bounds/off-by-one and weekly-chart accuracy related to start_of_week.
  • Improvements

    • Content-type exclusion guidance expanded: support for cpt: prefix (e.g., cpt:product) and case-insensitive entries.
  • Documentation

    • Readme and translation templates updated for the new release and guidance.
  • Tests

    • Many new end-to-end tests added for charts, bucketing, exclusions, custom DB, timezones and robustness.

parham tehrani and others added 13 commits March 16, 2026 18:57
The condition `$offset <= $this->points` was allowing negative offsets
and offsets equal to points (out of bounds) while potentially dropping
valid data. Changed to `$offset >= 0 && $offset < $this->points` to
properly validate array indices.

This fixes the issue where users see only a few records in charts
despite having hundreds in the database.

Ref: Ticket #14684
The condition `$offset <= $this->points` was allowing negative offsets
and offsets equal to points (out of bounds) while potentially dropping
valid data. Changed to `$offset >= 0 && $offset < $this->points` to
properly validate array indices.

This fixes the issue where users see only a few records in charts
despite having hundreds in the database.

Fixes #231
…p-slimstat/wp-slimstat into fix/issue-14684-chart-data-not-showing
…ulation

Validates the DataBuckets::addRow() off-by-one fix (DataBuckets.php:209)
and confirms the slimstat_custom_wpdb filter causes no read/write split.

- custom-db-simulator-mu-plugin.php: simulates external DB addon filter;
  uses $GLOBALS['wpdb'] to avoid PHP 8.2 fatal with same-named parameter
- setup.ts: register mu-plugin in MU_PLUGIN_MANIFEST
- databuckets-custom-db-accuracy.spec.ts: 3 tests covering default DB,
  custom DB filter active, and boundary edge case

Refs #231 / Support #14684
- custom-db-simulator-mu-plugin: reset wpdb->result via Reflection after
  clone to prevent "mysqli_result already closed" fatal (HTTP 500) caused
  by shallow clone sharing the protected result property with global wpdb

- databuckets spec Test 5: rewrite to accurately test what the code does —
  Chart.php always reads from global wpdb (wp_slim_stats), not wp_slimstat::$wpdb,
  so the custom DB filter affects write paths only; test now confirms the AJAX
  handler survives the filter active and data is still accurate

All 6 tests now pass (19.5s): Tests 1-6 PASS
Source: Support #14684 / GitHub #231
Tests for bot, CPT, permalink, and user exclusion filters.
Confirms issue #233: CPT exclusion requires cpt: prefix.
The ignore_content_types description now explains that custom post
types require the cpt: prefix (e.g., cpt:product). Without this,
users entering bare type names see exclusion silently fail.

Fixes #233
Validates that DataBuckets::initSeqWeek() uses DAY_NAMES[] instead of
jddayofweek(), which would fatal on servers without PHP ext-calendar.

- 4 tests: HTTP 200, sum accuracy, Sunday start-of-week, corrupted SOW fallback
- calendar-ext-simulator mu-plugin: throws RuntimeException from SlimStat\Helpers
  namespace, simulating ext-calendar absence via PHP namespace shadowing
- Uses nonce-helper AJAX endpoint for reliable nonce generation (avoids
  scraping slimstat_chart_vars which can fail with unusual start_of_week)
- beforeEach clears chart transients to prevent cached results across tests
…rage

Closes gaps identified in PR #232 impact analysis:
- Test 7 in databuckets-custom-db-accuracy.spec.ts verifies MONTHLY
  granularity chart sum equals DB count (the offset formula for MONTH
  differs from WEEK and was previously untested).
- New chart-sql-expression-validation.spec.ts (4 tests) verifies that
  valid default expressions pass the validateSqlExpression() whitelist
  and that arithmetic/injection payloads are rejected with success:false.
- Add try/finally to Test 3 (bot exclusion) so uninstallHeaderInjector()
  always runs even if an assertion throws
- Add flush_rewrite_rules() to CPT mu-plugin init callback so /product/{slug}/
  pretty permalinks resolve immediately without a 404
- Replace all waitForTimeout(4000) with expect.poll() across all 5 tests;
  inclusion tests poll until the record appears, exclusion tests poll to
  confirm the count/resource stays at the expected value
…ot-showing

Fix chart data not showing due to incorrect bounds check
…lidation

5 tests verifying the DataBuckets bounds fix (PR #232) addresses the
"wrong stats" report where daily/weekly charts showed zero for today.
@parhumm
Copy link
Copy Markdown
Contributor Author

parhumm commented Mar 17, 2026

E2E QA Report: Chart "Today Shows Zero" Regression

Simple Summary

  • What was tested: Whether the v5.4.4 release fixes the "wrong stats" bug where daily and weekly charts show zero pageviews for today and the current week.
  • Results: 5/5 tests pass — the fix correctly resolves the reported issue.
  • Root cause: Off-by-one bounds check in DataBuckets::addRow() silently dropped records at range boundaries into phantom buckets.
  • Fix verified: $offset >= 0 && $offset < $this->points (PR Fix chart data not showing due to incorrect bounds check #232) prevents data loss.
  • Verdict: PR Release v5.4.4 #234 (Release v5.4.4) is safe to merge and will fix this user's issue.

Trigger

Environment

Property Value
Plugin wp-slimstat v5.4.4
WordPress Local by Flywheel
Base URL http://localhost:10003
PHP 8.5 (via Local)
Test runner Playwright + WP-CLI eval-file

Issue Analysis

User Report

The user reported that after updating to the latest version:

  1. Daily chart: Today shows 0 pageviews, 0 unique visitors (yesterday was ~185)
  2. Weekly chart: Current week (March 16-17) shows 0 pageviews

Root Cause

DataBuckets::addRow() (line 209) had a bounds check $offset <= $this->points:

  • Datasets are 0-indexed arrays with $this->points entries (indices 0 to points-1)
  • The old check allowed writing to index $this->points (a phantom bucket with no label)
  • Records at range boundaries (today = last day, current week = last week) had offsets that could equal $this->points, causing them to be written to the phantom bucket
  • The chart rendering couldn't match phantom buckets to labels, so these records were silently dropped, showing as zero

Fix

Changed to $offset >= 0 && $offset < $this->points — proper array bounds checking that:

  1. Rejects negative offsets (pre-range records)
  2. Rejects offset == points (phantom bucket)
  3. Only accepts indices [0, points-1] — matching label count exactly

Test Results

# Test Status Details
1 Daily: today's records visible PASS DB=15, chart sum=15, today bucket=5, labels=28
2 Weekly: current week records visible PASS DB=14, chart sum=14, last week bucket=3, labels=5
3 28-day daily range accuracy PASS DB=18, chart sum=18, labels=28
4 Previous period comparison populated PASS current=8, prev=4, array lengths match
5 Cross-granularity consistency PASS DB=12, daily=12, weekly=12

Test 1: Daily chart — today's records visible (CORE REGRESSION)

  • Inserted 5 records for "today" (1 min ago) and 10 for "yesterday"
  • Chart correctly shows today=5 in the last daily bucket
  • v1 array length matches labels count (no phantom bucket)
  • This directly reproduces and validates the user's report

Test 2: Weekly chart — current partial week visible (CORE REGRESSION)

  • Inserted 3 records for today + 7 last week + 4 two weeks ago
  • Current week bucket shows 3 (not zero)
  • This directly reproduces the user's weekly chart showing zero

Test 3: 28-day daily range accuracy

  • 18 records spread across 9 different days in the 28-day range
  • Every record accounted for in chart sum

Test 4: Previous period comparison

  • Current period: 8 records, previous period: 4 records
  • Both datasets and datasets_prev correctly populated
  • Array lengths match (no misalignment)

Test 5: Cross-granularity consistency

  • Same 12 records produce identical totals in daily (12) and weekly (12) views
  • No data loss when switching granularities

Analytics Invariants

Metric Threshold Actual Status
Event loss (server) <= 0.05% 0% PASS
Duplicates <= 0.1% 0% PASS
Attribution correctness >= 99.5% 100% PASS
Phantom bucket presence 0 0 PASS

Verdict

PASS — The DataBuckets bounds fix in PR #232 correctly resolves the "Wrong stats" issue reported by acekin26. All 5 regression tests confirm:

  1. Today's records are visible in daily charts (not zero)
  2. Current partial week records are visible in weekly charts (not zero)
  3. No data loss across the full 28-day range
  4. Previous period comparison renders correctly
  5. Daily and weekly granularities produce consistent totals

The release PR #234 (v5.4.4) is safe to merge.

Spec Location

wp-slimstat/tests/e2e/chart-today-zero-regression.spec.ts

The weekly offset calculation was using ISO week numbers (date('W'))
which always start on Monday, regardless of WordPress start_of_week
setting. This caused data to be placed in the wrong bucket when
start_of_week was set to Sunday (0).

Now calculates the start-of-week date for both base and data point
timestamps using the WordPress start_of_week setting, ensuring
correct bucket placement.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e379cb78-e4e6-4e1d-94b0-5642766b02e3

📥 Commits

Reviewing files that changed from the base of the PR and between 42e4724 and b0ee25e.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • languages/wp-slimstat.pot
  • readme.txt
🚧 Files skipped from review as they are similar to previous changes (3)
  • languages/wp-slimstat.pot
  • readme.txt
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

Fixes weekly chart bucketing (week-start alignment and off-by-one bounds), updates the Content Types exclusion UI text to document the cpt: prefix and case-insensitivity, bumps plugin to 5.4.4, and adds many end-to-end tests and MU-plugin helpers for chart/databuckets/calendar/ext and exclusion behaviors.

Changes

Cohort / File(s) Summary
Version & Metadata
CHANGELOG.md, readme.txt, wp-slimstat.php, languages/wp-slimstat.pot
Bumped release to 5.4.4 and updated changelog/readme/POT metadata to list chart fixes and CPT guidance.
Config UI Text
admin/config/index.php
Updated ignore_content_types description to document cpt: prefix for custom post types (e.g., cpt:product) and that strings are case-insensitive.
Chart Bucketing Logic
src/Helpers/DataBuckets.php
Reworked WEEK offset calculation to use week-start-aligned timestamps via new getWeekStartTimestamp(); tightened bounds check to 0 <= offset < points; adjusted "today" detection to use week-start comparisons.
E2E Test Helpers & MU-Plugins
tests/e2e/helpers/...
tests/e2e/helpers/chart.ts, tests/e2e/helpers/setup.ts, tests/e2e/helpers/custom-db-simulator-mu-plugin.php, tests/e2e/helpers/calendar-ext-simulator-mu-plugin.php
Added shared chart utilities, WP-CLI chart fetch, DB helpers, and MU-plugin simulators (custom DB, calendar-ext); registered new MU-plugins in test manifest.
E2E Test Suites — Databuckets & Robustness
tests/e2e/databuckets-*.spec.ts, tests/e2e/chart-robustness.spec.ts, tests/e2e/chart-monthly-daily-granularity.spec.ts
New comprehensive E2E suites validating DataBuckets accuracy, custom-DB scenarios, ext-calendar fallback, timezone/edge-case robustness, and monthly/daily granularity.
E2E Test Suites — Weekly Bucketing / Regressions
tests/e2e/weekly-*.spec.ts, tests/e2e/chart-today-zero-regression.spec.ts, tests/e2e/chart-negative-regression.spec.ts
Added multiple weekly-bucketing regression tests including comparisons against previous/master behavior and start_of_week variants.
E2E Test Suites — Validation & Exclusions
tests/e2e/chart-sql-expression-validation.spec.ts, tests/e2e/exclusion-filters.spec.ts
Added SQL expression whitelist validation tests and exclusion-filter tests covering cpt: handling, bot/user/permalink exclusions.
Test Config & Comments
tests/e2e/global-setup.ts, tests/e2e/playwright.config.ts
Minor comment update in global setup; documented single-worker requirement in Playwright config due to TRUNCATE-based tests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

Poem

"I’m a rabbit who hopped through rows and time,
nudged weeks to start neat and fixed that offset crime.
CPTs now hide when tagged with a tiny prefix,
tests stand guard so buckets keep their tricks.
Version 5.4.4 — carrots, code, and rhyme."

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Release v5.4.4' directly matches the PR's primary purpose of releasing version 5.4.4, clearly summarizing the main change from the developer's perspective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch development
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
tests/e2e/helpers/custom-db-simulator-mu-plugin.php (1)

21-21: Use a site-scoped prefix in the simulator to avoid multisite cross-site leakage.

Using a fixed slimext_ prefix can cause table collisions if these tests run in multisite contexts.

♻️ Suggested refactor
-    $custom->prefix = 'slimext_';
+    $custom->prefix = is_multisite() && get_current_blog_id() > 1
+        ? 'slimext_' . get_current_blog_id() . '_'
+        : 'slimext_';
Based on learnings: Test for multisite compatibility; ensure data is stored per-site with no cross-site leakage; test network activate/deactivate.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/helpers/custom-db-simulator-mu-plugin.php` at line 21, The
simulator currently sets a fixed table prefix via $custom->prefix = 'slimext_',
which can collide across sites in multisite tests; change it to use a
site-scoped prefix by deriving it from WordPress (e.g. use
$wpdb->get_blog_prefix(get_current_blog_id()) or $wpdb->prefix) and append the
existing 'slimext_' suffix so tables are namespaced per-site; update any related
setup/teardown in the helper so network activate/deactivate and per-site cleanup
use the same derived prefix.
tests/e2e/databuckets-custom-db-accuracy.spec.ts (1)

148-157: Harden sumV1 against string-valued datasets.

If v1 values ever come through as strings, a + b can concatenate instead of sum. Coercing values to numbers makes this helper resilient.

Proposed refactor
 function sumV1(json: any): number {
+  const sumNumeric = (arr: unknown[]) => arr.reduce((a, b) => a + Number(b ?? 0), 0);
+
   // AJAX response: { success: true, data: { data: { datasets: { v1: [...] } } } }
   const ajaxDatasets = json?.data?.data?.datasets?.v1;
   if (Array.isArray(ajaxDatasets)) {
-    return (ajaxDatasets as number[]).reduce((a, b) => a + b, 0);
+    return sumNumeric(ajaxDatasets);
   }
   // Server-rendered: { datasets: { v1: [...] } }
   const serverDatasets = json?.datasets?.v1;
   if (Array.isArray(serverDatasets)) {
-    return (serverDatasets as number[]).reduce((a, b) => a + b, 0);
+    return sumNumeric(serverDatasets);
   }
   return 0;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts` around lines 148 - 157, The
sumV1 helper should coerce dataset entries to numbers to avoid string
concatenation: in function sumV1, when handling ajaxDatasets and serverDatasets,
map each entry to Number(entry) (or use unary +) and filter out NaN before
reducing, then use numeric reduce (e.g., (a,b)=>a+b,0) to return a correct
numeric sum; update references to ajaxDatasets and serverDatasets accordingly so
both paths handle string values robustly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts`:
- Around line 429-433: The epoch constants and comments for the two-month range
are inconsistent: update monthStart, monthEnd, feb15Ts, and mar10Ts so their
numeric Unix timestamps actually map to 2026-02-01 00:00:00 UTC, 2026-03-31
23:59:59 UTC, 2026-02-15 00:00:00 UTC, and 2026-03-10 00:00:00 UTC respectively,
and correct the inline comments for each constant to reflect the 2026 dates;
ensure monthEnd uses the exact end-of-day timestamp for March 31 (23:59:59) to
cover the month-end boundary in tests.

In `@tests/e2e/exclusion-filters.spec.ts`:
- Around line 46-48: The test currently calls flush_rewrite_rules() on every
init hook which is costly; change the logic so flush_rewrite_rules() is invoked
only once—either run it in a plugin activation/activation hook or perform a
one-time transient/option check inside the init handler (e.g., check and set a
transient or option like "my_plugin_rewrites_flushed" before calling
flush_rewrite_rules()), or move the call into the test
bootstrap/after-activation step or a WP-CLI command that runs once for the test
suite; update the code invoking init/flush_rewrite_rules() to use that one-time
pattern rather than flushing on every init.
- Around line 249-252: The test uses the wrong admin username ("parhumm")
causing credential mismatches with global setup; update the adminPage.fill call
that targets '#user_login' in tests/e2e/exclusion-filters.spec.ts (the line with
adminPage.fill('#user_login', ...)) and the same occurrence in
tests/e2e/outbound-report-display.spec.ts (around line 32) to use 'bashiri'
instead of 'parhumm' so the tests use the same user as
tests/e2e/global-setup.ts; leave the '#user_pass' value unchanged unless the
password in global-setup.ts also differs.

In `@tests/e2e/global-setup.ts`:
- Around line 49-55: The inline comment preceding the admin login is
stale—update the comment to match the actual username used in the login call;
locate the call to loginAndSave(baseURL, 'bashiri', 'testpass123',
path.join(AUTH_DIR, 'admin.json')) and change the comment that currently
mentions "parhumm" to refer to "bashiri" (or adjust the username in the function
call if the comment was correct and the code should be reverted), ensuring the
comment and the login arguments are consistent.

---

Nitpick comments:
In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts`:
- Around line 148-157: The sumV1 helper should coerce dataset entries to numbers
to avoid string concatenation: in function sumV1, when handling ajaxDatasets and
serverDatasets, map each entry to Number(entry) (or use unary +) and filter out
NaN before reducing, then use numeric reduce (e.g., (a,b)=>a+b,0) to return a
correct numeric sum; update references to ajaxDatasets and serverDatasets
accordingly so both paths handle string values robustly.

In `@tests/e2e/helpers/custom-db-simulator-mu-plugin.php`:
- Line 21: The simulator currently sets a fixed table prefix via $custom->prefix
= 'slimext_', which can collide across sites in multisite tests; change it to
use a site-scoped prefix by deriving it from WordPress (e.g. use
$wpdb->get_blog_prefix(get_current_blog_id()) or $wpdb->prefix) and append the
existing 'slimext_' suffix so tables are namespaced per-site; update any related
setup/teardown in the helper so network activate/deactivate and per-site cleanup
use the same derived prefix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1bfa30df-89f4-45b6-ada5-616fd24fbe33

📥 Commits

Reviewing files that changed from the base of the PR and between 4e80bdb and d06d6ec.

📒 Files selected for processing (15)
  • CHANGELOG.md
  • admin/config/index.php
  • languages/wp-slimstat.pot
  • readme.txt
  • src/Helpers/DataBuckets.php
  • tests/e2e/chart-sql-expression-validation.spec.ts
  • tests/e2e/chart-today-zero-regression.spec.ts
  • tests/e2e/databuckets-custom-db-accuracy.spec.ts
  • tests/e2e/databuckets-no-calendar-ext.spec.ts
  • tests/e2e/exclusion-filters.spec.ts
  • tests/e2e/global-setup.ts
  • tests/e2e/helpers/calendar-ext-simulator-mu-plugin.php
  • tests/e2e/helpers/custom-db-simulator-mu-plugin.php
  • tests/e2e/helpers/setup.ts
  • wp-slimstat.php

Comment on lines +429 to +433
// Fixed 2-month range: 2026-02-01 → 2026-03-31 UTC (past, cache-eligible range)
const monthStart = 1738368000; // 2026-02-01 00:00 UTC
const monthEnd = 1743379199; // 2026-03-31 23:59 UTC
const feb15Ts = 1739577600; // 2026-02-15 UTC (mid Feb)
const mar10Ts = 1741564800; // 2026-03-10 UTC (mid Mar)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Monthly range constants/comments are inconsistent and miss the stated end date.

Line 430–433 epochs map to 2025 dates, while comments say 2026, and monthEnd currently ends at 2025-03-30 23:59:59 UTC (not March 31). This makes the test intent ambiguous and weakens month-end boundary coverage.

Suggested correction
-    // Fixed 2-month range: 2026-02-01 → 2026-03-31 UTC (past, cache-eligible range)
-    const monthStart = 1738368000; // 2026-02-01 00:00 UTC
-    const monthEnd   = 1743379199; // 2026-03-31 23:59 UTC
-    const feb15Ts    = 1739577600; // 2026-02-15 UTC (mid Feb)
-    const mar10Ts    = 1741564800; // 2026-03-10 UTC (mid Mar)
+    // Fixed 2-month range: 2025-02-01 → 2025-03-31 UTC (past, cache-eligible range)
+    const monthStart = 1738368000; // 2025-02-01 00:00 UTC
+    const monthEnd   = 1743465599; // 2025-03-31 23:59:59 UTC
+    const feb15Ts    = 1739577600; // 2025-02-15 UTC (mid Feb)
+    const mar10Ts    = 1741564800; // 2025-03-10 UTC (mid Mar)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/databuckets-custom-db-accuracy.spec.ts` around lines 429 - 433, The
epoch constants and comments for the two-month range are inconsistent: update
monthStart, monthEnd, feb15Ts, and mar10Ts so their numeric Unix timestamps
actually map to 2026-02-01 00:00:00 UTC, 2026-03-31 23:59:59 UTC, 2026-02-15
00:00:00 UTC, and 2026-03-10 00:00:00 UTC respectively, and correct the inline
comments for each constant to reflect the 2026 dates; ensure monthEnd uses the
exact end-of-day timestamp for March 31 (23:59:59) to cover the month-end
boundary in tests.

Comment on lines +46 to +48
// Flush rewrite rules so /product/{slug}/ pretty permalinks resolve immediately.
flush_rewrite_rules();
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid flush_rewrite_rules() on every init hook — performance concern.

Calling flush_rewrite_rules() on every init is expensive and can cause performance issues. For tests, consider flushing once after plugin activation or using a one-time transient check.

📝 Suggested fix — flush only once
 add_action('init', function() {
     register_post_type('product', [
         'public'       => true,
         'label'        => 'Products',
         'has_archive'  => true,
         'rewrite'      => ['slug' => 'product'],
         'supports'     => ['title', 'editor'],
         'show_in_rest' => true,
     ]);
-    // Flush rewrite rules so /product/{slug}/ pretty permalinks resolve immediately.
-    flush_rewrite_rules();
 });
+
+// Flush rewrite rules once on activation
+add_action('init', function() {
+    if (get_transient('e2e_product_cpt_flush_done')) return;
+    flush_rewrite_rules();
+    set_transient('e2e_product_cpt_flush_done', true, 3600);
+}, 99);

Or simply call flush_rewrite_rules() separately via WP-CLI after installing the MU-plugin.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/exclusion-filters.spec.ts` around lines 46 - 48, The test currently
calls flush_rewrite_rules() on every init hook which is costly; change the logic
so flush_rewrite_rules() is invoked only once—either run it in a plugin
activation/activation hook or perform a one-time transient/option check inside
the init handler (e.g., check and set a transient or option like
"my_plugin_rewrites_flushed" before calling flush_rewrite_rules()), or move the
call into the test bootstrap/after-activation step or a WP-CLI command that runs
once for the test suite; update the code invoking init/flush_rewrite_rules() to
use that one-time pattern rather than flushing on every init.

Comment on lines +249 to +252
await adminPage.fill('#user_login', 'parhumm');
await adminPage.fill('#user_pass', 'testpass123');
await adminPage.click('#wp-submit');
await adminPage.waitForURL('**/wp-admin/**', { timeout: 30_000 });
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if both usernames are used consistently across e2e tests
echo "=== Checking admin usernames in e2e tests ==="
rg -n "parhumm|bashiri" --type=ts tests/e2e/

Repository: wp-slimstat/wp-slimstat

Length of output: 399


🏁 Script executed:

cat -n tests/e2e/global-setup.ts | sed -n '40,65p'

Repository: wp-slimstat/wp-slimstat

Length of output: 857


🏁 Script executed:

rg -n "user_login|user_pass" --type=ts tests/e2e/ | grep -E "(parhumm|bashiri|testpass)"

Repository: wp-slimstat/wp-slimstat

Length of output: 440


Credential mismatch — parhumm vs bashiri.

The admin username here is parhumm, but tests/e2e/global-setup.ts was updated to use bashiri. This will cause test failures if the environment only has the bashiri user. Additionally, tests/e2e/outbound-report-display.spec.ts:32 has the same issue.

Update both occurrences to use bashiri:

Diff
-    await adminPage.fill('#user_login', 'parhumm');
+    await adminPage.fill('#user_login', 'bashiri');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await adminPage.fill('#user_login', 'parhumm');
await adminPage.fill('#user_pass', 'testpass123');
await adminPage.click('#wp-submit');
await adminPage.waitForURL('**/wp-admin/**', { timeout: 30_000 });
await adminPage.fill('#user_login', 'bashiri');
await adminPage.fill('#user_pass', 'testpass123');
await adminPage.click('#wp-submit');
await adminPage.waitForURL('**/wp-admin/**', { timeout: 30_000 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/exclusion-filters.spec.ts` around lines 249 - 252, The test uses
the wrong admin username ("parhumm") causing credential mismatches with global
setup; update the adminPage.fill call that targets '#user_login' in
tests/e2e/exclusion-filters.spec.ts (the line with adminPage.fill('#user_login',
...)) and the same occurrence in tests/e2e/outbound-report-display.spec.ts
(around line 32) to use 'bashiri' instead of 'parhumm' so the tests use the same
user as tests/e2e/global-setup.ts; leave the '#user_pass' value unchanged unless
the password in global-setup.ts also differs.

parhumm and others added 3 commits March 17, 2026 17:55
Validates weekly chart DataBuckets offset calculation respects WordPress
start_of_week setting (Sunday/Monday/Saturday), cross-granularity
consistency, and week boundary behavior. 5/5 pass.
The toArray() method was using wp_date('YW', ...) to check if the
end date is in the current week, but 'YW' returns ISO week format
which always starts on Monday regardless of WordPress start_of_week.

Added getWeekStartTimestamp() helper method to calculate week start
respecting the WordPress setting, and refactored both addRow() and
toArray() to use this consistent logic.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests confirm DataBuckets::addRow() ISO week mismatch when
start_of_week is Sunday (0) or Saturday (6): data loss from
negative offsets and bucket misalignment.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (5)
tests/e2e/weekly-bucketing-last-week-count.spec.ts (5)

281-284: Weak assertion may miss bucketing errors.

Using toBeGreaterThanOrEqual(3) is a loose check. If there's a bug that causes extra data to land in this bucket (e.g., duplication), this assertion would still pass. Consider calculating and asserting the exact expected value.

🎯 Consider precise assertion
       // Second-to-last should contain Mar 14 (Sat) = part of Mar 8 (Sun) week
       // Plus any other data in that week
       const secondToLast = v1[v1.length - 2];
-      expect(secondToLast, `Second-to-last bucket should include Mar 14=3`).toBeGreaterThanOrEqual(3);
+      // With sow=0: Mar 8 (Sun) week contains Mar 8-14; Mar 14=3 is the only seeded day in that week
+      expect(secondToLast, `Second-to-last bucket should be exactly 3 (Mar 14 only)`).toBe(3);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts` around lines 281 - 284,
The assertion for the second-to-last bucket (variable secondToLast from array
v1) is too loose; compute the exact expected count for that bucket based on the
test input (including Mar 14 and any other known events that should fall into
the Mar 8 week) and replace the toBeGreaterThanOrEqual(3) check with a strict
equality assertion (e.g., expect(secondToLast).toEqual(EXACT_COUNT)). Update the
test to calculate EXACT_COUNT deterministically from the test fixtures or
hardcode the known correct sum so the test fails on duplication/mis-bucketing.

143-146: Unused helper function dayName.

The dayName function is defined but never called anywhere in the test file. Consider removing it to reduce dead code, or add a comment if it's intended for debugging.

🧹 Remove unused function
-/** Get day of week name for a timestamp */
-function dayName(ts: number): string {
-  return new Date(ts * 1000).toUTCString().split(',')[0];
-}
-
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts` around lines 143 - 146,
The helper function dayName is defined but never used; remove the dead code by
deleting the dayName(ts: number) function declaration (or, if you intend to keep
it for ad-hoc debugging, add a clear comment explaining its purpose and suppress
the unused-variable lint rule) so the file no longer contains the unused symbol
dayName.

210-212: Consider using Playwright's test.info() for structured test output.

Multiple console.log statements can clutter CI output. Playwright provides test.info().annotations or test.step() for structured logging that integrates better with reporters.

📊 Example using test.step for structured output
await test.step('Verify chart data', async () => {
  const v1 = getV1(json);
  const labels = getLabels(json);
  const chartSum = sumV1(json);
  
  test.info().annotations.push({ type: 'chart-data', description: `Labels: ${labels.join(', ')}` });
  test.info().annotations.push({ type: 'chart-data', description: `V1: ${v1.join(', ')}` });
  // assertions...
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts` around lines 210 - 212,
Replace the three console.log calls that print labels, v1 and the chart sum with
structured Playwright reporting: wrap the verification in a test.step('Verify
chart data', ...) and use test.info().annotations.push(...) (or
test.info().annotations = [...]) to add annotations for Labels, V1 and Chart sum
(referencing the local vars labels, v1, chartSum and totalExpected) so the
output integrates with reporters and avoids cluttering CI logs; ensure
assertions remain inside the step.

108-109: Hardcoded wp_ table prefix may break in alternative test environments.

The table name wp_slim_stats and wp_options are hardcoded. If the test environment uses a custom table prefix (e.g., wp_test_), these queries will fail silently or target the wrong tables.

Consider using a helper that reads the prefix from WP_ROOT/wp-config.php or an environment variable.

Also applies to: 116-118

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts` around lines 108 - 109,
The hardcoded table names ('INSERT INTO wp_slim_stats ...' and references to
'wp_options') will break when a WP table prefix differs; change these queries to
use a resolved table prefix by reading the prefix from wp-config.php or an
environment variable (e.g., process.env.WP_TABLE_PREFIX) and build table names
like `${prefix}slim_stats` and `${prefix}options` before executing them,
replacing the literal 'wp_slim_stats' and 'wp_options' strings in the test SQL
statements.

175-203: Test assumes default start_of_week=1 but doesn't explicitly set it.

Unlike Tests 2 and 4 which snapshot and explicitly set start_of_week, this test relies on the default being 1 (Monday). If a previous test run fails before restoring the option, or if the test environment has a different default, this test may produce unexpected results.

Consider adding explicit setup for consistency:

🔧 Explicitly set start_of_week for test isolation
   test('March 14 data lands in Mar 9 bucket, NOT in Mar 16 bucket (sow=1)', async () => {
+    // Ensure default Monday start
+    await snapshotOption('start_of_week');
+    await getPool().execute("UPDATE wp_options SET option_value = '1' WHERE option_name = 'start_of_week'");
+
+    try {
     // Seed: 3 hits on Mar 14 (Saturday), 2 hits on Mar 15 (Sunday),
     //        5 hits on Mar 16 (Monday), 4 hits on Mar 17 (Tuesday)
     // ... existing code ...
     console.log('Test 1 PASS — Mar 14 correctly in Mar 9 bucket, Mar 16-17 in last bucket');
+    } finally {
+      await restoreOption('start_of_week');
+    }
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts` around lines 175 - 203,
This test relies on the default start_of_week=1 but doesn't set it explicitly;
modify the test (the 'March 14 data lands...' case that calls utcMidnight,
insertRows and fetchChartData) to explicitly set start_of_week to 1 at test
setup and restore the original value at teardown using the same
snapshot/set/restore pattern used by Tests 2 and 4 so the test is isolated from
global defaults or prior failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts`:
- Around line 281-284: The assertion for the second-to-last bucket (variable
secondToLast from array v1) is too loose; compute the exact expected count for
that bucket based on the test input (including Mar 14 and any other known events
that should fall into the Mar 8 week) and replace the toBeGreaterThanOrEqual(3)
check with a strict equality assertion (e.g.,
expect(secondToLast).toEqual(EXACT_COUNT)). Update the test to calculate
EXACT_COUNT deterministically from the test fixtures or hardcode the known
correct sum so the test fails on duplication/mis-bucketing.
- Around line 143-146: The helper function dayName is defined but never used;
remove the dead code by deleting the dayName(ts: number) function declaration
(or, if you intend to keep it for ad-hoc debugging, add a clear comment
explaining its purpose and suppress the unused-variable lint rule) so the file
no longer contains the unused symbol dayName.
- Around line 210-212: Replace the three console.log calls that print labels, v1
and the chart sum with structured Playwright reporting: wrap the verification in
a test.step('Verify chart data', ...) and use test.info().annotations.push(...)
(or test.info().annotations = [...]) to add annotations for Labels, V1 and Chart
sum (referencing the local vars labels, v1, chartSum and totalExpected) so the
output integrates with reporters and avoids cluttering CI logs; ensure
assertions remain inside the step.
- Around line 108-109: The hardcoded table names ('INSERT INTO wp_slim_stats
...' and references to 'wp_options') will break when a WP table prefix differs;
change these queries to use a resolved table prefix by reading the prefix from
wp-config.php or an environment variable (e.g., process.env.WP_TABLE_PREFIX) and
build table names like `${prefix}slim_stats` and `${prefix}options` before
executing them, replacing the literal 'wp_slim_stats' and 'wp_options' strings
in the test SQL statements.
- Around line 175-203: This test relies on the default start_of_week=1 but
doesn't set it explicitly; modify the test (the 'March 14 data lands...' case
that calls utcMidnight, insertRows and fetchChartData) to explicitly set
start_of_week to 1 at test setup and restore the original value at teardown
using the same snapshot/set/restore pattern used by Tests 2 and 4 so the test is
isolated from global defaults or prior failures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a57b09f3-c6fb-46b3-9ace-1341b0092986

📥 Commits

Reviewing files that changed from the base of the PR and between d06d6ec and ef13bab.

📒 Files selected for processing (1)
  • tests/e2e/weekly-bucketing-last-week-count.spec.ts

parhumm added 4 commits March 17, 2026 19:22
…dev)

Targeted test for the exact screenshot scenario: weekly chart with
start_of_week=6 (Saturday), validating Mar 7-14 bucket contains only
Sat-Fri data, boundary split between Mar 13 (Fri) and Mar 14 (Sat),
and cross-granularity consistency.
…ssion

Tests daily buckets, monthly buckets, previous periods, month boundaries,
and cross-granularity consistency (daily=weekly=monthly) for sow=0,1,6.
Both branches pass 9/9 — confirms PR #235 weekly fix is scoped and safe.
…howing

Fix weekly chart not showing today's data
Test 1 assumed WP default of sow=1 but the site may have a different
value. Now explicitly sets and restores start_of_week like other tests.
@parhumm
Copy link
Copy Markdown
Contributor Author

parhumm commented Mar 17, 2026

E2E QA Report: Post-Merge Chart Suite Verification (PR #235 → development)

Branch: development @ 3c286884 (merged PR #235)
Date: 2026-03-17
Runner: Playwright, single worker (isolated DB)

Chart Test Results: 18/18 PASS

# Spec Test Result Key Values
D1 chart-monthly-daily Daily buckets match DB total sum=55, 28 labels, 11 non-zero
D2 chart-monthly-daily Daily totals identical across sow=1,0,6 all=70
D3 chart-monthly-daily Daily previous period populated curr=8, prev=12
M1 chart-monthly-daily Monthly buckets match DB total sum=125 [0,15,20,25,30,35]
M2 chart-monthly-daily Monthly totals across sow=1,0,6 all=60
M3 chart-monthly-daily Monthly previous period populated curr=10, prev=15
M4 chart-monthly-daily Feb 28 / Mar 1 month boundary Feb=100, Mar=200
X1 chart-monthly-daily Cross-gran daily=weekly=monthly (sow=6) all=63
X2 chart-monthly-daily Cross-gran daily=weekly=monthly (sow=0) all=63
W1 weekly-bucketing sow=1: Mar 14 in Mar 9 bucket [2,0,3,6,9] sum=20
W2 weekly-bucketing sow=0: Mar 14 in Mar 8, Mar 15-17 in Mar 15 [2,0,3,3,11] sum=19
W3 weekly-bucketing Daily vs weekly cross-granularity daily=33, weekly=33
W4 weekly-bucketing sow=6: Mar 14 starts new week [2,0,0,4,8] sum=14
W5 weekly-bucketing 28 days × 1 hit/day no data loss [4,7,7,7,3] sum=28
S1 sow6-mar7-validation Mar 7 bucket = Sat-Fri only Mar7=45, Mar14=90, sum=160
S2 sow6-mar7-validation Previous period populated (not 0) curr=25, prev=20
S3 sow6-mar7-validation Daily vs weekly match (sow=6) daily=50, weekly=50
S4 sow6-mar7-validation Boundary: Mar 13 (Fri) ≠ Mar 14 (Sat) Mar13=100 in bucket 3, Mar14=200 in bucket 4

Summary

Granularity Tests Pass Fail
Daily 3 3 0
Weekly 9 9 0
Monthly 4 4 0
Cross-granularity 2 2 0
Total 18 18 0

Verification

  • PR Fix weekly chart not showing today's data #235 weekly fix (getWeekStartTimestamp()) merged cleanly, no regressions
  • Daily and monthly granularity completely unaffected by the weekly fix
  • All start_of_week values (0, 1, 6) produce correct bucket assignments
  • Cross-granularity invariant holds: daily_sum == weekly_sum == monthly_sum for all sow values
  • Previous period data correctly populated across all granularities

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/Helpers/DataBuckets.php (1)

216-216: Match the repo's Yoda rule for the lower-bound check.

Line 216 is the only changed literal comparison here that still uses non-Yoda ordering.

♻️ Suggested tweak
-        if ($offset >= 0 && $offset < $this->points) {
+        if ( 0 <= $offset && $offset < $this->points ) {
As per coding guidelines "Use Yoda Conditions: `if ( 'value' === $var )`."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Helpers/DataBuckets.php` at line 216, Change the lower-bound comparison
in the conditional that checks $offset so it uses Yoda-style ordering: replace
the `$offset >= 0` part with `0 <= $offset` while keeping the upper-bound check
`$offset < $this->points` intact; update the condition around the `$offset`
variable in the DataBuckets class (the `if ($offset >= 0 && $offset <
$this->points)` statement) to `if (0 <= $offset && $offset < $this->points)`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Helpers/DataBuckets.php`:
- Line 292: The weekly "today" comparison uses PHP's system time instead of
WordPress time and should compare getWeekStartTimestamp($this->end) against the
WordPress-aware current timestamp; change the right-hand call to use
current_time('timestamp') (i.e. compare $this->getWeekStartTimestamp($this->end)
=== $this->getWeekStartTimestamp(current_time('timestamp'))) and keep the
fallback wp_date(..., wp_timezone()) usage; also update the bounds check
condition from if ($offset >= 0 && $offset < $this->points) to the Yoda-style if
(0 <= $offset && $offset < $this->points) so offset/points logic uses the
required style.

In `@tests/e2e/chart-monthly-daily-granularity.spec.ts`:
- Around line 205-216: The test mutates the global WP option via
getPool().execute(...) and currently calls restoreOption('start_of_week') only
on the success path; wrap the mutation and assertions in a try/finally so
restoreOption('start_of_week') always runs (i.e., call
snapshotOption('start_of_week'), then try { update via getPool().execute(...);
run fetchChartData(...), expect(...), record results via sumArr(getV1(json)); }
finally { await restoreOption('start_of_week'); }). Apply the same try/finally
pattern to the M2 block mentioned (the other occurrence at lines 331-342) so
both mutations always restore the option.

In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts`:
- Around line 177-178: The test mutates the WP option via
getPool().execute("UPDATE wp_options SET option_value = '1' WHERE option_name =
'start_of_week'") but only restores it on the success path; wrap the mutation in
a try/finally (or mirror the pattern used in the other tests) and call
snapshotOption('start_of_week') or execute the original value restore in the
finally block to guarantee restoration on failure; update both occurrences (the
current block and the similar occurrence around line ~234) and reference
snapshotOption and getPool().execute when making the changes.

In `@tests/e2e/weekly-start-of-week-bucketing.spec.ts`:
- Around line 319-356: The test currently only checks total sums but not that
buckets differ; after calling setStartOfWeek(0) and setStartOfWeek(1) and
computing v1Sow0/v1Sow1 (from fetchChartData and getV1), add assertions that
verify the bucket indices/counts reflect the described split: for sow=0 ensure
Saturday and Sunday counts are in different weekly buckets (e.g. two separate
non-zero entries in v1Sow0 corresponding to the two days inserted), and for
sow=1 ensure Saturday and Sunday are aggregated into the same weekly bucket (a
single entry in v1Sow1 that equals dbCount for those two days or at least a
matching combined count). Use getV1, sumV1, fetchChartData, setStartOfWeek,
mostRecentDayOfWeek and insertRows to locate and validate the bucket
distribution rather than only sums.

---

Nitpick comments:
In `@src/Helpers/DataBuckets.php`:
- Line 216: Change the lower-bound comparison in the conditional that checks
$offset so it uses Yoda-style ordering: replace the `$offset >= 0` part with `0
<= $offset` while keeping the upper-bound check `$offset < $this->points`
intact; update the condition around the `$offset` variable in the DataBuckets
class (the `if ($offset >= 0 && $offset < $this->points)` statement) to `if (0
<= $offset && $offset < $this->points)`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 374bca94-7c81-4ee4-8ae3-f79dc0d3d575

📥 Commits

Reviewing files that changed from the base of the PR and between ef13bab and a1fb4ca.

📒 Files selected for processing (5)
  • src/Helpers/DataBuckets.php
  • tests/e2e/chart-monthly-daily-granularity.spec.ts
  • tests/e2e/weekly-bucketing-last-week-count.spec.ts
  • tests/e2e/weekly-bucketing-sow6-mar7-validation.spec.ts
  • tests/e2e/weekly-start-of-week-bucketing.spec.ts

'datasets' => $this->datasets,
'datasets_prev' => $this->datasetsPrev,
'today' => 'WEEK' === $this->gran && wp_date('YW', $this->end, wp_timezone()) === wp_date('YW', time(), wp_timezone()) ? str_replace("'", '', $this->labels[count($this->labels) - 1]) : wp_date($this->labelFormat, time(), wp_timezone()),
'today' => 'WEEK' === $this->gran && $this->getWeekStartTimestamp($this->end) === $this->getWeekStartTimestamp(time()) ? str_replace("'", '', $this->labels[count($this->labels) - 1]) : wp_date($this->labelFormat, time(), wp_timezone()),
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "---- weekly today calculation ----"
sed -n '286,294p' src/Helpers/DataBuckets.php
echo
echo "---- timezone-related calls under src/ ----"
rg -n "date_default_timezone_set|wp_date\(|wp_timezone\(" src

Repository: wp-slimstat/wp-slimstat

Length of output: 1548


🏁 Script executed:

#!/bin/bash
echo "---- getWeekStartTimestamp method ----"
rg -A 15 "function getWeekStartTimestamp" src/Helpers/DataBuckets.php

echo
echo "---- Line 216 context ----"
sed -n '210,220p' src/Helpers/DataBuckets.php

echo
echo "---- Full DataBuckets.php lines 241-251 (new helper mentioned) ----"
sed -n '241,251p' src/Helpers/DataBuckets.php

Repository: wp-slimstat/wp-slimstat

Length of output: 1901


🏁 Script executed:

#!/bin/bash
echo "---- Searching for date_default_timezone_set in entire repo ----"
rg -n "date_default_timezone_set" .

echo
echo "---- Searching for timezone initialization patterns ----"
rg -n "timezone_string|date_default_timezone" . --type php | head -20

Repository: wp-slimstat/wp-slimstat

Length of output: 634


🏁 Script executed:

#!/bin/bash
echo "---- Line 216 and surrounding context ----"
sed -n '214,220p' src/Helpers/DataBuckets.php

Repository: wp-slimstat/wp-slimstat

Length of output: 408


Fix timezone handling in weekly date boundary detection and apply Yoda condition formatting.

Line 292's weekly "today" comparison uses getWeekStartTimestamp() on both sides, which relies on PHP's default timezone via date() and strtotime() instead of WordPress timezone. Without an explicit date_default_timezone_set() call in the codebase, if the site's WordPress timezone differs from the system/PHP default, the week-start boundary comparison evaluates incorrectly, causing the "today" marker to flip on the wrong clock. The fallback branch correctly uses wp_date(..., wp_timezone()), but the condition should match this precision.

Additionally, line 216's bounds check violates the Yoda condition guideline: if ($offset >= 0 && $offset < $this->points) should be if (0 <= $offset && $offset < $this->points).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Helpers/DataBuckets.php` at line 292, The weekly "today" comparison uses
PHP's system time instead of WordPress time and should compare
getWeekStartTimestamp($this->end) against the WordPress-aware current timestamp;
change the right-hand call to use current_time('timestamp') (i.e. compare
$this->getWeekStartTimestamp($this->end) ===
$this->getWeekStartTimestamp(current_time('timestamp'))) and keep the fallback
wp_date(..., wp_timezone()) usage; also update the bounds check condition from
if ($offset >= 0 && $offset < $this->points) to the Yoda-style if (0 <= $offset
&& $offset < $this->points) so offset/points logic uses the required style.

Comment on lines +161 to +169
function mostRecentDayOfWeek(dayOfWeek: number, refTs: number): number {
const refDate = new Date(refTs * 1000);
const currentDay = refDate.getUTCDay(); // 0=Sun
const diff = (currentDay - dayOfWeek + 7) % 7;
// If diff is 0, it's today — use it
const targetDate = new Date(refDate);
targetDate.setUTCDate(targetDate.getUTCDate() - diff);
targetDate.setUTCHours(12, 0, 0, 0); // noon to avoid DST edge cases
return Math.floor(targetDate.getTime() / 1000);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
from datetime import datetime, timedelta, timezone

ref = datetime(2026, 3, 22, 8, 0, 0, tzinfo=timezone.utc)  # Sunday 08:00 UTC
target_dow = 0  # Sunday
current_dow = (ref.weekday() + 1) % 7  # emulate JS getUTCDay()
diff = (current_dow - target_dow + 7) % 7
target = ref - timedelta(days=diff)
target = target.replace(hour=12, minute=0, second=0, microsecond=0)

print("ref   :", ref.isoformat())
print("target:", target.isoformat())
print("future?", target > ref)
PY
echo
sed -n '157,170p' tests/e2e/weekly-start-of-week-bucketing.spec.ts

Repository: wp-slimstat/wp-slimstat

Length of output: 741


🏁 Script executed:

# Find and examine Test 5 and rangeEnd context
rg -n "Test 5|describe.*Test 5|it.*Test 5|it\(" tests/e2e/weekly-start-of-week-bucketing.spec.ts -A 5 | head -100

Repository: wp-slimstat/wp-slimstat

Length of output: 546


🏁 Script executed:

# Look for rangeEnd and the test structure around line 319-356
wc -l tests/e2e/weekly-start-of-week-bucketing.spec.ts

Repository: wp-slimstat/wp-slimstat

Length of output: 119


🏁 Script executed:

# Get the test file to see Test 5 context
sed -n '280,400p' tests/e2e/weekly-start-of-week-bucketing.spec.ts

Repository: wp-slimstat/wp-slimstat

Length of output: 3097


🏁 Script executed:

# Find rangeStart and rangeEnd definitions
rg -n "rangeStart|rangeEnd" tests/e2e/weekly-start-of-week-bucketing.spec.ts | head -40

Repository: wp-slimstat/wp-slimstat

Length of output: 636


🏁 Script executed:

# Look at the top of the file to see test setup
head -160 tests/e2e/weekly-start-of-week-bucketing.spec.ts

Repository: wp-slimstat/wp-slimstat

Length of output: 5190


mostRecentDayOfWeek() returns a future timestamp when called before noon UTC on its target weekday.

When refTs is on Sunday before 12:00 UTC and dayOfWeek=0, diff=0, so setUTCHours(12, 0, 0, 0) shifts the timestamp forward. In Test 5, line 321 calls mostRecentDayOfWeek(0, now) to seed Sunday records, which will fall outside rangeEnd when the test runs before noon UTC on a Sunday.

Additionally, Test 5 (lines 319–357) verifies only that totals match across sow=0 and sow=1, not the bucket distribution—it should assert that Saturday and Sunday land in different weeks under sow=0 but the same week under sow=1.

Comment on lines +319 to +356
test('Sunday records land in correct week bucket for sow=0 vs sow=1', async () => {
// Find the most recent Sunday timestamp
const sundayTs = mostRecentDayOfWeek(0, now); // 0 = Sunday
// And the Saturday before it
const saturdayTs = sundayTs - day;

// Insert records on Saturday and Sunday
await insertRows(saturdayTs, 4, 'boundary-sat');
await insertRows(sundayTs, 3, 'boundary-sun');

const dbCount = await countTestRows();
expect(dbCount).toBe(7);

// With sow=0 (Sunday): Sunday starts a NEW week
setStartOfWeek(0);
const jsonSow0 = fetchChartData(rangeStart, rangeEnd, 'weekly');
expect(jsonSow0?.success).toBe(true);
const v1Sow0 = getV1(jsonSow0);
const sumSow0 = sumV1(jsonSow0);
expect(sumSow0).toBe(dbCount);

// With sow=1 (Monday): Sunday is the LAST day of the week
setStartOfWeek(1);
const jsonSow1 = fetchChartData(rangeStart, rangeEnd, 'weekly');
expect(jsonSow1?.success).toBe(true);
const v1Sow1 = getV1(jsonSow1);
const sumSow1 = sumV1(jsonSow1);
expect(sumSow1).toBe(dbCount);

// Both must account for all records
expect(sumSow0).toBe(sumSow1);

// The bucket distribution should differ: with sow=0, Sunday starts a new week
// so Saturday (4) and Sunday (3) are in different weeks.
// With sow=1, Saturday and Sunday are in the same week (both after Monday).
// We can't assert exact bucket indices without knowing the day-of-week of 'now',
// but we can verify the total is correct and no records are lost.
console.log(`Test 5 PASS — boundary: DB=${dbCount}, sow0=${JSON.stringify(v1Sow0)}, sow1=${JSON.stringify(v1Sow1)}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This test never asserts the bucket split it describes.

After sow=0 and sow=1 are fetched, the only hard checks are total sums. If Sunday is still bucketed identically in both runs, this test still passes.

🎯 Assert the actual split
-    // We can't assert exact bucket indices without knowing the day-of-week of 'now',
-    // but we can verify the total is correct and no records are lost.
+    const nonZeroSow0 = v1Sow0.filter((v) => v > 0).sort((a, b) => a - b);
+    const nonZeroSow1 = v1Sow1.filter((v) => v > 0).sort((a, b) => a - b);
+    expect(nonZeroSow0, 'sow=0 should split Saturday and Sunday into separate weekly buckets').toEqual([3, 4]);
+    expect(nonZeroSow1, 'sow=1 should keep Saturday and Sunday in the same weekly bucket').toEqual([7]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/weekly-start-of-week-bucketing.spec.ts` around lines 319 - 356, The
test currently only checks total sums but not that buckets differ; after calling
setStartOfWeek(0) and setStartOfWeek(1) and computing v1Sow0/v1Sow1 (from
fetchChartData and getV1), add assertions that verify the bucket indices/counts
reflect the described split: for sow=0 ensure Saturday and Sunday counts are in
different weekly buckets (e.g. two separate non-zero entries in v1Sow0
corresponding to the two days inserted), and for sow=1 ensure Saturday and
Sunday are aggregated into the same weekly bucket (a single entry in v1Sow1 that
equals dbCount for those two days or at least a matching combined count). Use
getV1, sumV1, fetchChartData, setStartOfWeek, mostRecentDayOfWeek and insertRows
to locate and validate the bucket distribution rather than only sums.

…ive regression (41/41 pass)

Extract shared chart utilities into helpers/chart.ts (-888 lines duplication),
add explicit start_of_week isolation to all specs, fix global-setup to use
admin user, add chart-robustness.spec.ts (timezone, browser AJAX, edge cases)
and chart-negative-regression.spec.ts (git worktree master vs development).
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
tests/e2e/chart-monthly-daily-granularity.spec.ts (2)

95-106: ⚠️ Potential issue | 🟡 Minor

D2 should wrap sow mutations in try/finally for guaranteed cleanup.

If any assertion fails in the loop, restoreOption('start_of_week') at line 106 won't execute, leaving the option dirty for subsequent tests.

🔧 Wrap in try/finally
     await snapshotOption('start_of_week');
-
     const results: Record<string, number> = {};
-
-    for (const sow of ['1', '0', '6']) {
-      await getPool().execute("UPDATE wp_options SET option_value = ? WHERE option_name = 'start_of_week'", [sow]);
-      const json = fetchChartData(rangeStart, rangeEnd, 'daily');
-      expect(json?.success).toBe(true);
-      results[`sow${sow}`] = sumArr(getV1(json));
-    }
-
-    await restoreOption('start_of_week');
+    try {
+      for (const sow of ['1', '0', '6']) {
+        await getPool().execute("UPDATE wp_options SET option_value = ? WHERE option_name = 'start_of_week'", [sow]);
+        const json = fetchChartData(rangeStart, rangeEnd, 'daily');
+        expect(json?.success).toBe(true);
+        results[`sow${sow}`] = sumArr(getV1(json));
+      }
+    } finally {
+      await restoreOption('start_of_week');
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/chart-monthly-daily-granularity.spec.ts` around lines 95 - 106, The
test mutates the WP option via getPool().execute in the for-loop and calls
restoreOption('start_of_week') after the loop, which can be skipped if an
assertion fails; wrap the mutation+assert loop in a try/finally so
restoreOption('start_of_week') always runs: call snapshotOption('start_of_week')
before the try, perform the for-loop where you set the option
(getPool().execute), call fetchChartData and assertions (fetchChartData, getV1,
sumArr) inside the try, and place restoreOption('start_of_week') in the finally
block to guarantee cleanup even on failures.

219-230: ⚠️ Potential issue | 🟡 Minor

M2 should wrap sow mutations in try/finally for guaranteed cleanup.

Same issue as D2 — if assertions fail, restoreOption won't execute.

🔧 Wrap in try/finally
     await snapshotOption('start_of_week');
-
     const results: Record<string, number> = {};
-
-    for (const sow of ['1', '0', '6']) {
-      await getPool().execute("UPDATE wp_options SET option_value = ? WHERE option_name = 'start_of_week'", [sow]);
-      const json = fetchChartData(rangeStart, rangeEnd, 'monthly');
-      expect(json?.success).toBe(true);
-      results[`sow${sow}`] = sumArr(getV1(json));
-    }
-
-    await restoreOption('start_of_week');
+    try {
+      for (const sow of ['1', '0', '6']) {
+        await getPool().execute("UPDATE wp_options SET option_value = ? WHERE option_name = 'start_of_week'", [sow]);
+        const json = fetchChartData(rangeStart, rangeEnd, 'monthly');
+        expect(json?.success).toBe(true);
+        results[`sow${sow}`] = sumArr(getV1(json));
+      }
+    } finally {
+      await restoreOption('start_of_week');
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/chart-monthly-daily-granularity.spec.ts` around lines 219 - 230,
The test mutates the WP option 'start_of_week' with getPool().execute inside a
loop but doesn't guarantee restoreOption runs if an assertion fails; wrap the
mutation+assertion loop in a try/finally: call await
snapshotOption('start_of_week') before the try, perform the for-loop (updates,
fetchChartData, expect, results accumulation) inside the try, and ensure await
restoreOption('start_of_week') is invoked in the finally block so cleanup always
occurs; reference functions: snapshotOption, getPool().execute, fetchChartData,
getV1, sumArr, restoreOption.
tests/e2e/weekly-bucketing-last-week-count.spec.ts (1)

43-92: ⚠️ Potential issue | 🟡 Minor

Test 1 should wrap mutations in try/finally to ensure cleanup.

Unlike Tests 2-5 which properly use try/finally, Test 1 calls restoreOption('start_of_week') only on the success path (line 91). If any assertion fails, start_of_week remains dirty for subsequent tests.

🔧 Wrap in try/finally
   test('March 14 data lands in Mar 9 bucket, NOT in Mar 16 bucket (sow=1)', async () => {
     await snapshotOption('start_of_week');
     await getPool().execute("UPDATE wp_options SET option_value = '1' WHERE option_name = 'start_of_week'");
 
+    try {
       const mar14 = utcMidnight('2026-03-14');
       // ... rest of test logic ...
       console.log('Test 1 PASS — Mar 14 correctly in Mar 9 bucket, Mar 16-17 in last bucket');
-
-    await restoreOption('start_of_week');
+    } finally {
+      await restoreOption('start_of_week');
+    }
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts` around lines 43 - 92, The
test "March 14 data lands in Mar 9 bucket, NOT in Mar 16 bucket (sow=1)" mutates
global state (calls snapshotOption and getPool().execute to set start_of_week)
but only calls restoreOption('start_of_week') on the success path; wrap the body
that seeds rows and assertions in a try/finally block so
restoreOption('start_of_week') runs in finally, ensuring cleanup even if
assertions fail, keeping snapshotOption(), getPool().execute(... 'start_of_week'
...), insertRows(...), fetchChartData(...), and related assertions inside the
try and calling restoreOption in the finally.
🧹 Nitpick comments (4)
tests/e2e/chart-robustness.spec.ts (2)

44-51: Use parameterized queries for SQL to avoid injection risk.

The SET time_zone statement uses string interpolation which, while safe with the current hardcoded test values, establishes an unsafe pattern. Consider using parameterized execution or validating the input format.

♻️ Safer approach with validation
 async function setTimezone(mysqlTz: string, wpGmtOffset: string): Promise<void> {
+  // Validate timezone format (+HH:MM or -HH:MM)
+  if (!/^[+-]\d{2}:\d{2}$/.test(mysqlTz)) {
+    throw new Error(`Invalid timezone format: ${mysqlTz}`);
+  }
   const pool = getPool();
-  await pool.execute(`SET time_zone = '${mysqlTz}'`);
+  await pool.execute(`SET time_zone = ?`, [mysqlTz]);
   await pool.execute(
     "UPDATE wp_options SET option_value = ? WHERE option_name = 'gmt_offset'",
     [wpGmtOffset]
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/chart-robustness.spec.ts` around lines 44 - 51, The setTimezone
function currently interpolates mysqlTz into the SQL string (in setTimezone and
pool.execute calls), which is unsafe; update setTimezone to avoid direct string
interpolation by using a parameterized query for the SET time_zone statement
(use pool.execute with a placeholder and pass mysqlTz) or, if the DB driver
disallows parameterizing SET, strictly validate/whitelist mysqlTz before
interpolating (e.g., allow only offsets like '+00:00' or known timezone names)
and keep the wp_options update using the existing parameterized placeholder for
wpGmtOffset; ensure references to setTimezone, getPool, and pool.execute are
updated accordingly.

294-297: Consider replacing fixed timeout with explicit wait condition.

page.waitForTimeout(3000) is brittle and slows tests unnecessarily. Consider waiting for a specific DOM change or network idle state after granularity selection.

♻️ More robust wait
     await granSelect.selectOption(targetGran);
 
-    // Wait for AJAX update
-    await page.waitForTimeout(3000);
+    // Wait for chart data to update after AJAX
+    await page.waitForFunction(
+      (el, oldData) => el?.getAttribute('data-data') !== oldData,
+      { timeout: 10_000 },
+      await chartDataEl.elementHandle(),
+      initialDataRaw
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/chart-robustness.spec.ts` around lines 294 - 297, Replace the
brittle fixed delay call page.waitForTimeout(3000) with an explicit wait that
targets the post-granularity-change condition: for example, wait for the
specific DOM update (use page.waitForSelector or page.waitForFunction to detect
the chart element/text/attribute that changes after granularity selection) or
wait for the relevant network response using page.waitForResponse matching the
request URL/endpoint; update the test around the granularity selection step in
chart-robustness.spec.ts to use one of these explicit waits instead of
page.waitForTimeout(3000).
tests/e2e/chart-negative-regression.spec.ts (1)

61-147: runBucketTest function is defined but never called.

This function generates PHP scripts for comparing master vs current DataBuckets behavior, but neither NEG-1 nor NEG-2 actually invokes it. The tests instead use fetchChartData() from helpers and verify behavior through code content inspection.

Consider removing this dead code or documenting why it's retained for future use.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/chart-negative-regression.spec.ts` around lines 61 - 147,
runBucketTest is dead code (defined but never invoked by NEG-1/NEG-2) — either
remove the entire runBucketTest function from chart-negative-regression.spec.ts
to eliminate unused code, or explicitly document why it’s retained (add a
top-of-function comment referencing NEG-1 and NEG-2 and that the tests use
fetchChartData() instead) so future readers know it’s intentionally kept; locate
the runBucketTest declaration and delete it or add the explanatory comment
accordingly.
tests/e2e/helpers/chart.ts (1)

19-50: PHP code generation interpolates values directly into strings.

While startTs and endTs are numbers and granularity comes from test code (not user input), the pattern of interpolating values into PHP strings is fragile. The server-side validation in Chart.php provides defense-in-depth, but consider using a more structured approach.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/helpers/chart.ts` around lines 19 - 50, The test currently injects
startTs, endTs and granularity directly into the phpCode string inside
fetchChartData, which is fragile; change fetchChartData to serialize the
parameters into a single safe payload (e.g., JSON or base64) and embed only that
encoded string in phpCode, then decode/json_decode it in the PHP snippet and set
$_POST/$_REQUEST from the decoded structure before calling
\\SlimStat\\Modules\\Chart::ajaxFetchChartData(); specifically, stop
interpolating ${startTs}, ${endTs} and '${granularity}' directly and instead
write something like a JSON-encoded argsPayload in the tmpFile and have the PHP
use json_decode to populate $_POST['args'], $_POST['granularity'] and $_REQUEST
values so values are passed structurally and not string-interpolated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/e2e/chart-monthly-daily-granularity.spec.ts`:
- Around line 95-106: The test mutates the WP option via getPool().execute in
the for-loop and calls restoreOption('start_of_week') after the loop, which can
be skipped if an assertion fails; wrap the mutation+assert loop in a try/finally
so restoreOption('start_of_week') always runs: call
snapshotOption('start_of_week') before the try, perform the for-loop where you
set the option (getPool().execute), call fetchChartData and assertions
(fetchChartData, getV1, sumArr) inside the try, and place
restoreOption('start_of_week') in the finally block to guarantee cleanup even on
failures.
- Around line 219-230: The test mutates the WP option 'start_of_week' with
getPool().execute inside a loop but doesn't guarantee restoreOption runs if an
assertion fails; wrap the mutation+assertion loop in a try/finally: call await
snapshotOption('start_of_week') before the try, perform the for-loop (updates,
fetchChartData, expect, results accumulation) inside the try, and ensure await
restoreOption('start_of_week') is invoked in the finally block so cleanup always
occurs; reference functions: snapshotOption, getPool().execute, fetchChartData,
getV1, sumArr, restoreOption.

In `@tests/e2e/weekly-bucketing-last-week-count.spec.ts`:
- Around line 43-92: The test "March 14 data lands in Mar 9 bucket, NOT in Mar
16 bucket (sow=1)" mutates global state (calls snapshotOption and
getPool().execute to set start_of_week) but only calls
restoreOption('start_of_week') on the success path; wrap the body that seeds
rows and assertions in a try/finally block so restoreOption('start_of_week')
runs in finally, ensuring cleanup even if assertions fail, keeping
snapshotOption(), getPool().execute(... 'start_of_week' ...), insertRows(...),
fetchChartData(...), and related assertions inside the try and calling
restoreOption in the finally.

---

Nitpick comments:
In `@tests/e2e/chart-negative-regression.spec.ts`:
- Around line 61-147: runBucketTest is dead code (defined but never invoked by
NEG-1/NEG-2) — either remove the entire runBucketTest function from
chart-negative-regression.spec.ts to eliminate unused code, or explicitly
document why it’s retained (add a top-of-function comment referencing NEG-1 and
NEG-2 and that the tests use fetchChartData() instead) so future readers know
it’s intentionally kept; locate the runBucketTest declaration and delete it or
add the explanatory comment accordingly.

In `@tests/e2e/chart-robustness.spec.ts`:
- Around line 44-51: The setTimezone function currently interpolates mysqlTz
into the SQL string (in setTimezone and pool.execute calls), which is unsafe;
update setTimezone to avoid direct string interpolation by using a parameterized
query for the SET time_zone statement (use pool.execute with a placeholder and
pass mysqlTz) or, if the DB driver disallows parameterizing SET, strictly
validate/whitelist mysqlTz before interpolating (e.g., allow only offsets like
'+00:00' or known timezone names) and keep the wp_options update using the
existing parameterized placeholder for wpGmtOffset; ensure references to
setTimezone, getPool, and pool.execute are updated accordingly.
- Around line 294-297: Replace the brittle fixed delay call
page.waitForTimeout(3000) with an explicit wait that targets the
post-granularity-change condition: for example, wait for the specific DOM update
(use page.waitForSelector or page.waitForFunction to detect the chart
element/text/attribute that changes after granularity selection) or wait for the
relevant network response using page.waitForResponse matching the request
URL/endpoint; update the test around the granularity selection step in
chart-robustness.spec.ts to use one of these explicit waits instead of
page.waitForTimeout(3000).

In `@tests/e2e/helpers/chart.ts`:
- Around line 19-50: The test currently injects startTs, endTs and granularity
directly into the phpCode string inside fetchChartData, which is fragile; change
fetchChartData to serialize the parameters into a single safe payload (e.g.,
JSON or base64) and embed only that encoded string in phpCode, then
decode/json_decode it in the PHP snippet and set $_POST/$_REQUEST from the
decoded structure before calling
\\SlimStat\\Modules\\Chart::ajaxFetchChartData(); specifically, stop
interpolating ${startTs}, ${endTs} and '${granularity}' directly and instead
write something like a JSON-encoded argsPayload in the tmpFile and have the PHP
use json_decode to populate $_POST['args'], $_POST['granularity'] and $_REQUEST
values so values are passed structurally and not string-interpolated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d16c5434-7e1c-4672-9836-5d12ac073f89

📥 Commits

Reviewing files that changed from the base of the PR and between a1fb4ca and 42e4724.

📒 Files selected for processing (10)
  • tests/e2e/chart-monthly-daily-granularity.spec.ts
  • tests/e2e/chart-negative-regression.spec.ts
  • tests/e2e/chart-robustness.spec.ts
  • tests/e2e/chart-today-zero-regression.spec.ts
  • tests/e2e/global-setup.ts
  • tests/e2e/helpers/chart.ts
  • tests/e2e/playwright.config.ts
  • tests/e2e/weekly-bucketing-last-week-count.spec.ts
  • tests/e2e/weekly-bucketing-sow6-mar7-validation.spec.ts
  • tests/e2e/weekly-start-of-week-bucketing.spec.ts
✅ Files skipped from review due to trivial changes (1)
  • tests/e2e/playwright.config.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/e2e/weekly-start-of-week-bucketing.spec.ts
  • tests/e2e/weekly-bucketing-sow6-mar7-validation.spec.ts

@parhumm
Copy link
Copy Markdown
Contributor Author

parhumm commented Mar 17, 2026

E2E Chart Test Suite — 41/41 Pass

Full chart E2E suite consolidated and expanded. All 41 tests pass in 34.4s (single run, no flakes).

Test Matrix

Spec Tests Coverage
chart-today-zero-regression 5 Daily/weekly visibility, prev period, cross-granularity
chart-monthly-daily-granularity 9 Monthly buckets, daily sum, sow independence, prev period, boundaries
weekly-bucketing-last-week-count 5 sow=1/0/6 bucket placement, cross-granularity, 28-day no-loss
weekly-bucketing-sow6-mar7-validation 4 Mar 7-14 bucket isolation, prev period, boundary split
weekly-start-of-week-bucketing 5 sow=0/1/6 current week, daily vs weekly, boundary
chart-robustness 11 Timezone offsets (4), browser AJAX (2), edge cases (5)
chart-negative-regression 2 Git worktree master vs development: date(W) bug, <= off-by-one
Total 41

Key Changes (commit 42e4724)

  • helpers/chart.ts: Shared utilities extracted from 5 specs (−888 lines duplication)
  • Explicit start_of_week isolation: All specs snapshot/restore sow
  • chart-robustness.spec.ts (new): Timezone offsets, browser AJAX parity, midnight/month boundaries, concurrent visits, sub-second spacing
  • chart-negative-regression.spec.ts (new): Proves master branch bugs via git worktree checkout
  • global-setup.ts: Fixed to authenticate as administrator (was editor)

Gaps Covered

  1. Duplicated helpers → helpers/chart.ts
  2. Implicit sow assumptions → explicit snapshot/restore
  3. No timezone tests → TZ-1 through TZ-4
  4. No browser AJAX tests → AJAX-1, AJAX-2
  5. No negative regression → NEG-1, NEG-2 (worktree)
  6. No edge cases → EDGE-1 through EDGE-5

@parhumm
Copy link
Copy Markdown
Contributor Author

parhumm commented Mar 17, 2026

E2E Chart Test Suite — 41/41 Pass

Full chart E2E suite consolidated and expanded. All 41 tests pass in 34.4s (single run, no flakes).

Test Matrix

Spec Tests Coverage
chart-today-zero-regression 5 Daily/weekly visibility, prev period, cross-granularity
chart-monthly-daily-granularity 9 Monthly buckets, daily sum, sow independence, prev period, boundaries
weekly-bucketing-last-week-count 5 sow=1/0/6 bucket placement, cross-granularity, 28-day no-loss
weekly-bucketing-sow6-mar7-validation 4 Mar 7-14 bucket isolation, prev period, boundary split
weekly-start-of-week-bucketing 5 sow=0/1/6 current week, daily vs weekly, boundary
chart-robustness 11 Timezone offsets (4), browser AJAX (2), edge cases (5)
chart-negative-regression 2 Git worktree master vs development: date(W) bug, <= off-by-one
Total 41

Key Changes (commit 42e4724)

  • helpers/chart.ts: Shared utilities extracted from 5 specs (−888 lines duplication)
  • Explicit start_of_week isolation: All specs snapshot/restore sow
  • chart-robustness.spec.ts (new): Timezone offsets, browser AJAX parity, midnight/month boundaries, concurrent visits, sub-second spacing
  • chart-negative-regression.spec.ts (new): Proves master branch bugs via git worktree checkout
  • global-setup.ts: Fixed to authenticate as administrator (was editor)

Gaps Covered

  1. Duplicated helpers → helpers/chart.ts
  2. Implicit sow assumptions → explicit snapshot/restore
  3. No timezone tests → TZ-1 through TZ-4
  4. No browser AJAX tests → AJAX-1, AJAX-2
  5. No negative regression → NEG-1, NEG-2 (worktree)
  6. No edge cases → EDGE-1 through EDGE-5

@parhumm parhumm merged commit 1d44090 into master Mar 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant