Skip to content

Refactor: Optimize module management with local caching#2815

Open
mrabbani wants to merge 3 commits intodevelopfrom
refactor/e2e
Open

Refactor: Optimize module management with local caching#2815
mrabbani wants to merge 3 commits intodevelopfrom
refactor/e2e

Conversation

@mrabbani
Copy link
Member

@mrabbani mrabbani commented Jul 28, 2025

Summary

Refactors module management functionality to improve performance by implementing local caching and preventing unnecessary API calls.

Changes

  • Enhanced getAllModules to automatically save modules locally
  • Added checkIsModuleActiveFromLocal method for single module status checking
  • Updated activateModules and deactivateModules to check local state before API calls
  • Simplified getAllModuleIds by removing duplicate file writing logic

Benefits

  • Prevents unnecessary API calls when modules are already in desired state
  • Better local caching with always up-to-date JSON file
  • Cleaner code with improved method naming
  • More reliable state checking logic

Files Changed

  • tests/pw/utils/apiUtils.ts

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability of the "Add new product" button detection for vendors.
    • Adjusted navigation to direct users more accurately to the vendor products page.
  • New Features

    • Introduced local caching for module data to optimize performance and reduce redundant operations.
  • Chores

    • Updated file path handling for authentication storage to use absolute paths for better consistency.
    • Disabled email verification in test setup for smoother automated testing.
    • Added a short wait after vendor login during test authentication to improve stability.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 28, 2025

Walkthrough

This update introduces local caching for module activation state in API utilities, modifies navigation and selectors for product page interactions, adjusts authentication test setup to disable email verification, and changes how storage state file paths are resolved in test data. No new public API signatures are introduced, but several method implementations are updated.

Changes

Cohort / File(s) Change Summary
Product Page Navigation & Selector
tests/pw/pages/productsPage.ts, tests/pw/pages/selectors.ts
Modified navigation target in getProductEditNonce to vendor products page and broadened the XPath selector for the "Add new product" link for more flexible matching.
Test Authentication Setup
tests/pw/tests/e2e/_auth.setup.ts
Added disabling of email verification in test setup and introduced a wait after vendor login in the authentication test.
API Utilities: Module Activation Caching
tests/pw/utils/apiUtils.ts
Added local caching for module activation state, new methods for reading/checking module state from cache, and optimized activation/deactivation logic to skip redundant API calls. Updated method signatures to reflect possible null returns.
Test Data Path Resolution
tests/pw/utils/testData.ts
Changed storage state file paths from relative strings to absolute paths resolved at runtime using path.resolve.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test Script
    participant ApiUtils as ApiUtils
    participant FileSystem as Local JSON File
    participant Server as API Server

    Test->>ApiUtils: activateModules(moduleIds)
    ApiUtils->>FileSystem: getAllModulesFromLocalJson()
    FileSystem-->>ApiUtils: Module list (cached)
    alt All modules already active
        ApiUtils-->>Test: [null, null]
    else Some modules inactive
        ApiUtils->>Server: Activate modules API call
        Server-->>ApiUtils: API response
        ApiUtils->>Server: getAllModules()
        Server-->>ApiUtils: Updated module list
        ApiUtils->>FileSystem: Save modules.json
        ApiUtils-->>Test: [APIResponse, responseBody]
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Possibly related PRs

Suggested labels

Test Automation

Poem

A bunny with code in its paws
Hopped through selectors and cache with no pause.
Modules now check before they leap,
Storage paths resolved nice and neat.
With tests set up and emails off,
This rabbit reviews with a satisfied scoff! 🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/e2e

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@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: 2

🧹 Nitpick comments (4)
tests/pw/pages/selectors.ts (1)

3878-3878: Consider re-scoping the XPath to avoid accidental matches

The new selector is more tolerant of the optional “+” prefix, which is great.
However, by starting with //a from the document root it now matches any anchor containing “Add new product”. If the phrase ever appears outside the products widget, the test might interact with the wrong element.

- addNewProduct: '//a[contains(normalize-space(.), "Add new product")]',
+ addNewProduct: '//div[@class="dashboard-widget products"]//a[contains(normalize-space(.), "Add new product")]',

Scoping the search to the dashboard-widget products container keeps the flexibility while preventing false positives.

tests/pw/utils/testData.ts (1)

85-102: Consolidate path resolution to avoid multiple require calls

Each call to require('path').resolve incurs an unnecessary module lookup and clutters the object literal. Cache the resolved auth directory once and reuse it – this improves readability and keeps the file DRY.

-            storageState: require('path').resolve(__dirname, '../playwright/.auth/adminStorageState.json'),
+            storageState: `${AUTH_DIR}/adminStorageState.json`,
...
-            storageState: require('path').resolve(__dirname, '../playwright/.auth/vendorStorageState.json'),
+            storageState: `${AUTH_DIR}/vendorStorageState.json`,
...
-            storageState: require('path').resolve(__dirname, '../playwright/.auth/vendor2StorageState.json'),
+            storageState: `${AUTH_DIR}/vendor2StorageState.json`,
...
-            storageState: require('path').resolve(__dirname, '../playwright/.auth/customerStorageState.json'),
+            storageState: `${AUTH_DIR}/customerStorageState.json`,
...
-            storageState: require('path').resolve(__dirname, '../playwright/.auth/customer2StorageState.json'),
+            storageState: `${AUTH_DIR}/customer2StorageState.json`,

Add once near the top of the file (outside the diff context):

import path from 'node:path';

const AUTH_DIR = path.resolve(__dirname, '../playwright/.auth');

This single constant is cheap, clear and eliminates five duplicate require invocations.

tests/pw/utils/apiUtils.ts (2)

858-863: Consider async file operations and error handling.

The local caching implementation is good, but consider these improvements:

  1. Use async file operations: fs.writeFileSync blocks the event loop. Consider using fs.promises.writeFile or fs.writeFile with proper async/await.

  2. Add error handling: File operations can fail due to permissions, disk space, etc.

  3. Path robustness: The relative path ../playwright/modules.json assumes a specific directory structure that could be fragile.

-        // Save modules to local JSON file
-        fs.writeFileSync(
-            path.resolve(__dirname, '../playwright/modules.json'),
-            JSON.stringify(responseBody, null, 2)
-        );
+        // Save modules to local JSON file
+        try {
+            await fs.promises.writeFile(
+                path.resolve(__dirname, '../playwright/modules.json'),
+                JSON.stringify(responseBody, null, 2)
+            );
+        } catch (error) {
+            console.warn('Failed to save modules to local cache:', error.message);
+        }

875-883: Good error handling, consider async operations.

The method correctly handles file read errors by returning an empty array. However, consider using async file operations for consistency and better performance.

-    async getAllModulesFromLocalJson(): Promise<{ id: string; active: boolean }[]> {
-        try {
-            const allModules = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../playwright/modules.json'), 'utf8'));
-            return allModules;
-        } catch (error) {
-            // Return empty array if file doesn't exist or is invalid
-            return [];
-        }
-    }
+    async getAllModulesFromLocalJson(): Promise<{ id: string; active: boolean }[]> {
+        try {
+            const data = await fs.promises.readFile(path.resolve(__dirname, '../playwright/modules.json'), 'utf8');
+            return JSON.parse(data);
+        } catch (error) {
+            // Return empty array if file doesn't exist or is invalid
+            return [];
+        }
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93c4270 and 99cb0a0.

📒 Files selected for processing (5)
  • tests/pw/pages/productsPage.ts (1 hunks)
  • tests/pw/pages/selectors.ts (1 hunks)
  • tests/pw/tests/e2e/_auth.setup.ts (3 hunks)
  • tests/pw/utils/apiUtils.ts (3 hunks)
  • tests/pw/utils/testData.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/pw/pages/productsPage.ts (1)
tests/pw/utils/testData.ts (1)
  • data (63-2914)
🔇 Additional comments (6)
tests/pw/tests/e2e/_auth.setup.ts (1)

9-9: LGTM!

The import is correctly added to support the email verification disabling functionality used later in the file.

tests/pw/pages/productsPage.ts (1)

617-617: LGTM! Navigation optimization improves efficiency.

The change from navigating to the vendor dashboard home page to directly navigating to the products page is a logical optimization. Since the method needs to extract the product edit nonce from the "add new product" link, going directly to the products page (dashboard/products) eliminates an unnecessary navigation step compared to the previous dashboard home page (dashboard) approach.

This aligns well with the PR objectives of optimizing performance and reducing unnecessary operations.

tests/pw/utils/apiUtils.ts (4)

6-7: LGTM! Appropriate imports for file operations.

The fs and path imports are necessary for the local caching functionality being implemented.


887-892: LGTM! Clean implementation with proper TypeScript patterns.

The method correctly uses array.find() with optional chaining and nullish coalescing to safely check module activation status, defaulting to false when the module isn't found.


920-942: LGTM! Consistent implementation with activateModules.

The deactivateModules method follows the same optimization pattern as activateModules, maintaining consistency in the codebase. The logic correctly checks for already inactive modules and avoids redundant API calls.

The same cache staleness considerations mentioned for activateModules apply here as well.


895-917: activateModules: cache-first logic verified, no bypass calls detected

The rg search across .ts/.js files confirmed that all module activation/deactivation operations go through these apiUtils methods—no other code paths modify module state without hitting the same cache-checking logic. Your cache-first optimization stands solid.

• No direct activateModule/deactivateModule calls found outside of apiUtils.
• All E2E and API tests invoke activateModules/deactivateModules, followed by the local refresh via getAllModules.

Recommendation: to guard against rare cache staleness (e.g. external modifications), consider adding a configurable TTL or on-demand cache refresh before the local-check step.


setup.beforeAll(async () => {
apiUtils = new ApiUtils(await request.newContext());
await dbUtils.setOptionValue(dbData.dokan.optionName.emailVerification, { ...dbData.dokan.emailVerificationSettings, enabled: 'off' });
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify email verification setting doesn't affect other test suites.

The implementation correctly disables email verification for test automation, which is a good practice to avoid complications during user creation.

Please verify that this database option change doesn't persist across different test suites or affect parallel test execution:


🏁 Script executed:

#!/bin/bash
# Description: Check if other test files rely on email verification being enabled
# and verify test isolation

# Search for other references to email verification in tests
rg -A 3 -B 3 "emailVerification|email.*verification" --type ts --type js tests/

# Check if there are test cleanup or reset mechanisms
rg -A 5 "afterAll|teardown|cleanup" tests/pw/tests/e2e/

Length of output: 54736


We’ve confirmed that _auth.setup.ts disables email verification in beforeAll but never resets it in afterAll. This will leave email verification off for all subsequent tests that hit the same database.

Action Required: reset email verification in _auth.setup.ts teardown

  • In tests/pw/tests/e2e/_auth.setup.ts, add a call to restore the original settings in afterAll.

Suggested patch:

--- tests/pw/tests/e2e/_auth.setup.ts
@@ setup.afterAll(async () => {
-   await apiUtils.dispose();
+   // restore email verification to default
+   await dbUtils.setOptionValue(
+     dbData.dokan.optionName.emailVerification,
+     dbData.dokan.emailVerificationSettings
+   );
+   await apiUtils.dispose();
 });

This ensures isolation and prevents cross-test pollution.

🤖 Prompt for AI Agents
In tests/pw/tests/e2e/_auth.setup.ts at line 18, the code disables email
verification in beforeAll but does not reset it in afterAll, causing potential
test pollution. To fix this, add an afterAll block that calls
dbUtils.setOptionValue with the original email verification settings to restore
them after tests complete, ensuring test isolation.

setup('authenticate vendor', { tag: ['@lite'] }, async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.login(data.vendor, data.auth.vendorAuthFile);
await page.waitForTimeout(500);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace hard-coded wait with proper element/state waiting.

Hard-coded waitForTimeout(500) is brittle and makes tests unnecessarily slow. This indicates a race condition that should be addressed with proper waiting mechanisms.

Consider replacing with one of these approaches:

-        await page.waitForTimeout(500);
+        // Wait for navigation to complete
+        await page.waitForLoadState('networkidle');

Or wait for specific elements that need to be ready:

-        await page.waitForTimeout(500);
+        // Wait for the element needed by getProductEditNonce
+        await page.waitForSelector('[data-nonce], .wp-admin, #wpadminbar', { state: 'visible' });

Or if waiting for URL navigation:

-        await page.waitForTimeout(500);
+        // Wait for navigation to vendor dashboard/products page
+        await page.waitForURL('**/wp-admin/**');
📝 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 page.waitForTimeout(500);
// Wait for navigation to complete
await page.waitForLoadState('networkidle');
Suggested change
await page.waitForTimeout(500);
// Wait for the element needed by getProductEditNonce
await page.waitForSelector('[data-nonce], .wp-admin, #wpadminbar', { state: 'visible' });
Suggested change
await page.waitForTimeout(500);
// Wait for navigation to vendor dashboard/products page
await page.waitForURL('**/wp-admin/**');
🤖 Prompt for AI Agents
In tests/pw/tests/e2e/_auth.setup.ts at line 82, replace the hard-coded
waitForTimeout(500) with a more reliable waiting method such as waiting for a
specific element to appear or a particular state to be reached. Identify the
element or condition that indicates readiness and use page.waitForSelector or
page.waitForFunction accordingly to avoid brittle and slow tests caused by fixed
delays.

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