@fishawack/lab-env 4.43.0 → 4.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## Changelog
2
2
 
3
+ ### 4.44.0 (2025-08-17)
4
+
5
+ #### Features
6
+
7
+ * added capture webdriverio ai file ([66ef769](https://bitbucket.org/fishawackdigital/lab-env/commits/66ef7691dd48ba209d1425a27b03cd1fe20205a6))
8
+ * enable ssh cloning via php container ([cb47b41](https://bitbucket.org/fishawackdigital/lab-env/commits/cb47b417a832d998d82f4c1e1faef0c2ab33f67f))
9
+
10
+ ### 4.44.0-beta.1 (2025-08-15)
11
+
12
+ #### Features
13
+
14
+ * added capture webdriverio ai file ([66ef769](https://bitbucket.org/fishawackdigital/lab-env/commits/66ef7691dd48ba209d1425a27b03cd1fe20205a6))
15
+ * enable ssh cloning via php container ([cb47b41](https://bitbucket.org/fishawackdigital/lab-env/commits/cb47b417a832d998d82f4c1e1faef0c2ab33f67f))
16
+
3
17
  ### 4.43.0 (2025-08-10)
4
18
 
5
19
  #### Features
@@ -0,0 +1,232 @@
1
+ ---
2
+ applyTo: "_Node/capture.js,_Node/capture-*.js,_Node/capture/**/*.js"
3
+ ---
4
+
5
+ # Project Overview
6
+
7
+ This project uses WebDriverIO v9 for automated screenshot capture across multiple browsers, viewport sizes, and device emulations. The capture system is designed to generate consistent visual regression testing screenshots by hooking into a test runner that iterates through configured pages and viewport configurations.
8
+
9
+ The `_Node/capture.js` file serves as the entry point for custom capture logic, allowing projects to extend the default screenshot behavior with page-specific interactions, element manipulation, and additional test scenarios.
10
+
11
+ ## Folder Structure
12
+
13
+ ```
14
+ _Node/
15
+ ├── capture.js # Main capture customization file
16
+ ├── capture-*.js # Additional capture files (if needed)
17
+ └── capture/ # Capture-related modules directory
18
+ └── **/*.js # Nested capture modules
19
+ ```
20
+
21
+ The capture system automatically loads the first available file:
22
+
23
+ 1. `_Node/level-0/capture.js` (if exists)
24
+ 2. `_Node/capture.js` (fallback)
25
+
26
+ ## Libraries and Frameworks
27
+
28
+ ### WebDriverIO v9
29
+
30
+ - **Browser Control**: Uses WebDriverIO's browser object for page interaction
31
+ - **Element Selection**: Supports CSS selectors and XPath for element targeting
32
+ - **Screenshot Capture**: Utilizes BiDi capabilities for full-page and viewport-only screenshots
33
+ - **Device Emulation**: Supports both manual viewport sizing and device emulation profiles
34
+
35
+ ### Test Runner Integration
36
+
37
+ - Built on a test framework using `describe()` and `it()` blocks
38
+ - All custom interactions must be wrapped in `it()` test blocks
39
+ - Tests run asynchronously after being mapped out synchronously
40
+
41
+ ## Coding Standards
42
+
43
+ ### Module Structure
44
+
45
+ ```javascript
46
+ module.exports = {
47
+ // Called after viewport resize, before page iteration
48
+ size: (capture) => {
49
+ // Add dynamic pages to capture.page.array
50
+ // Access to capture.size properties
51
+ },
52
+
53
+ // Called after default page capture for each page
54
+ page: (capture) => {
55
+ // Wrap all interactions in it() blocks
56
+ // Access to capture.page and capture.screenshot
57
+ },
58
+ };
59
+ ```
60
+
61
+ ### Error Handling
62
+
63
+ - Let screenshot processes fail if expected selectors don't exist
64
+ - Failing tests indicate markup changes that need attention
65
+ - Avoid silent failures that could mask site issues
66
+
67
+ ### Asynchronous Operations
68
+
69
+ ```javascript
70
+ // Always use async/await for browser interactions
71
+ it("Test Description", async () => {
72
+ await browser.click(".selector");
73
+ await browser.pause(1000);
74
+ await capture.screenshot.call();
75
+ });
76
+ ```
77
+
78
+ ### Element Waiting
79
+
80
+ ```javascript
81
+ // Wait for specific elements before interaction
82
+ await $(".selector").waitForExist(50000);
83
+
84
+ // Use hard-coded waits sparingly
85
+ await browser.pause(1000);
86
+ ```
87
+
88
+ ### Page-Specific Logic
89
+
90
+ ```javascript
91
+ // Target specific pages using route comparison
92
+ if (capture.page.route === "/") {
93
+ // Homepage-only logic
94
+ }
95
+
96
+ if (capture.page.route === "/products") {
97
+ // Products page logic
98
+ }
99
+ ```
100
+
101
+ ## UI Guidelines
102
+
103
+ ### Screenshot Capture Methods
104
+
105
+ #### Full Page Screenshots (Default)
106
+
107
+ ```javascript
108
+ // Captures entire scrollable page content
109
+ await capture.screenshot.call();
110
+ // or explicitly
111
+ await capture.screenshot.call(false);
112
+ ```
113
+
114
+ #### Viewport-Only Screenshots
115
+
116
+ ```javascript
117
+ // Captures only visible viewport area
118
+ await capture.screenshot.call(true);
119
+ ```
120
+
121
+ ### Common UI Interaction Patterns
122
+
123
+ #### Removing Overlay Elements
124
+
125
+ ```javascript
126
+ it("Remove Modal Overlays", async () => {
127
+ await browser.execute(() => {
128
+ // Remove ISI trays
129
+ const isi = document.querySelector(".cmp-isi-tray");
130
+ if (isi) isi.remove();
131
+
132
+ // Remove cookie banners
133
+ const cookies = document.querySelector(".cookie-banner");
134
+ if (cookies) cookies.remove();
135
+
136
+ // Remove any modal overlays
137
+ const modals = document.querySelectorAll(".modal-overlay, .popup");
138
+ modals.forEach((modal) => modal.remove());
139
+ });
140
+ });
141
+ ```
142
+
143
+ #### Carousel Interaction
144
+
145
+ ```javascript
146
+ it("Capture Carousel States", async () => {
147
+ const slides = await $$(".carousel-slide");
148
+
149
+ for (let i = 0; i < slides.length; i++) {
150
+ await browser.click(".carousel-next");
151
+ await browser.pause(500); // Allow transition
152
+ await capture.screenshot.call(true); // Viewport only
153
+ }
154
+ });
155
+ ```
156
+
157
+ #### Accordion Toggling
158
+
159
+ ```javascript
160
+ it("Capture Accordion States", async () => {
161
+ const accordions = await $$(".accordion-header");
162
+
163
+ for (const accordion of accordions) {
164
+ await accordion.click();
165
+ await browser.pause(300); // Allow expansion
166
+ await capture.screenshot.call(true); // Viewport only
167
+ }
168
+ });
169
+ ```
170
+
171
+ #### Navigation States
172
+
173
+ ```javascript
174
+ it("Navigation Menu", async () => {
175
+ // Open navigation
176
+ await browser.click(".js-menu");
177
+ await $(".navigation").waitForDisplayed();
178
+
179
+ // Capture viewport only to avoid scrolling
180
+ await capture.screenshot.call(true);
181
+ });
182
+ ```
183
+
184
+ ### Dynamic Page Generation
185
+
186
+ ```javascript
187
+ size: (capture) => {
188
+ // Add dynamic routes from router configuration
189
+ const dynamicRoutes = require("../_Build/js/libs/routes.js");
190
+
191
+ dynamicRoutes.forEach((route) => {
192
+ capture.page.array.push(route.path);
193
+ });
194
+
195
+ // Add programmatically generated pages
196
+ const productIds = ["product-1", "product-2", "product-3"];
197
+ productIds.forEach((id) => {
198
+ capture.page.array.push(`/products/${id}`);
199
+ });
200
+ };
201
+ ```
202
+
203
+ ### Responsive Element Handling
204
+
205
+ ```javascript
206
+ it("Handle Responsive Elements", async () => {
207
+ // Check if mobile navigation exists
208
+ const mobileNav = await $(".mobile-nav");
209
+ if (await mobileNav.isDisplayed()) {
210
+ await mobileNav.click();
211
+ await capture.screenshot.call(true);
212
+ }
213
+
214
+ // Check if desktop navigation exists
215
+ const desktopNav = await $(".desktop-nav");
216
+ if (await desktopNav.isDisplayed()) {
217
+ await desktopNav.click();
218
+ await capture.screenshot.call(true);
219
+ }
220
+ });
221
+ ```
222
+
223
+ ### Best Practices
224
+
225
+ - Use descriptive `it()` block names for clear test identification
226
+ - Screenshot naming is handled automatically - don't override
227
+ - Prefer viewport screenshots (`true`) for UI state variations
228
+ - Use full page screenshots (`false`/omitted) for complete page captures
229
+ - Wait for elements/animations to complete before capturing
230
+ - Remove interfering UI elements (modals, banners) before main capture
231
+ - Leverage `capture.page.route` for page-specific logic
232
+ - Add dynamic pages in the `size()` method to ensure they're captured across all configurations
package/globals.js CHANGED
@@ -557,18 +557,20 @@ if (!existsSync(path.join(cwd, "stylelint.config.js"))) {
557
557
  );
558
558
  }
559
559
 
560
+ // Copy AI instructions file
561
+ const destDir = path.join(cwd, ".github/instructions");
562
+ if (!existsSync(destDir)) {
563
+ mkdirSync(destDir, { recursive: true });
564
+ }
565
+
560
566
  // Copy AI instructions file
561
567
  if (
562
568
  (platform === "laravel" && process.env.VERSION_LARAVEL === "10") ||
563
569
  platform === "php"
564
570
  ) {
565
- const destDir = path.join(cwd, ".github/copilot");
566
- if (!existsSync(destDir)) {
567
- mkdirSync(destDir, { recursive: true });
568
- }
569
571
  copyFileSync(
570
572
  path.join(__dirname, "_Ai/laravel-12.md"),
571
- path.join(destDir, "instructions.md"),
573
+ path.join(destDir, "laravel.instructions.md"),
572
574
  );
573
575
  }
574
576
 
@@ -577,16 +579,17 @@ if (
577
579
  pkg?.dependencies?.["vue"] &&
578
580
  semver.satisfies(semver.coerce(pkg.dependencies["vue"]), "3.x")
579
581
  ) {
580
- const destDir = path.join(cwd, ".github/copilot");
581
- if (!existsSync(destDir)) {
582
- mkdirSync(destDir, { recursive: true });
583
- }
584
582
  copyFileSync(
585
583
  path.join(__dirname, "_Ai/vue-3.md"),
586
- path.join(destDir, "instructions.md"),
584
+ path.join(destDir, "vue.instructions.md"),
587
585
  );
588
586
  }
589
587
 
588
+ copyFileSync(
589
+ path.join(__dirname, "_Ai/webdriveriov9-capture.md"),
590
+ path.join(destDir, "webdriverio-capture.instructions.md"),
591
+ );
592
+
590
593
  // If docker-compose.yml exists in project _Docker folder append to end
591
594
  let localOverride = "";
592
595
  if (existsSync(path.join(cwd, "_Docker/docker-compose.yml"))) {
@@ -36,6 +36,7 @@ services:
36
36
  volumes:
37
37
  - $CWD/:/app
38
38
  - vendor:/app/vendor
39
+ - $FW_DIR/.ssh:/home/php/.ssh
39
40
  environment:
40
41
  - FW_ROOT=${FW_ROOT:-}
41
42
  - USER_UID=${USER_UID:-0}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishawack/lab-env",
3
- "version": "4.43.0",
3
+ "version": "4.44.0",
4
4
  "description": "Docker manager for FW",
5
5
  "main": "cli.js",
6
6
  "scripts": {