skopix 2.0.60 → 2.0.61

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.
@@ -3952,6 +3952,36 @@ async function startStepTester(testerId, url, selector, mode, steps) {
3952
3952
  const page = await ctx.newPage();
3953
3953
 
3954
3954
  if (mode === 'preview' && steps && steps.length > 0) {
3955
+ // Register expose functions FIRST before addInitScript so they're available on DOMContentLoaded
3956
+ await ctx.exposeFunction('__skopixPreviewRun', async ({ index }) => {
3957
+ const s = steps[index];
3958
+ if (!s) return;
3959
+ await page.evaluate((i, status) => { if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, null, status); }, index, `Running step ${index+1}/${steps.length}...`).catch(()=>{});
3960
+ const result = await executeStepTesterAction(page, { selector: s.stableSelector||s.selector, action: s.action, value: s.value||'', assertType: s.assertType });
3961
+ await page.evaluate((i, r, total) => {
3962
+ if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, r, r.passed ? (i+1 >= total ? `✓ All ${total} steps passed!` : `✓ Step ${i+1} passed`) : `✗ Step ${i+1} failed`);
3963
+ }, index, result, steps.length).catch(()=>{});
3964
+ });
3965
+
3966
+ await ctx.exposeFunction('__skopixPreviewRunAll', async ({ fromIndex }) => {
3967
+ for (let i = fromIndex; i < steps.length; i++) {
3968
+ const s = steps[i];
3969
+ await page.evaluate((i, total) => { if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, null, `Running step ${i+1}/${total}...`); }, i, steps.length).catch(()=>{});
3970
+ const result = await executeStepTesterAction(page, { selector: s.stableSelector||s.selector, action: s.action, value: s.value||'', assertType: s.assertType });
3971
+ await page.evaluate((i, r, total) => {
3972
+ if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, r, r.passed ? (i+1 >= total ? `✓ All ${total} steps passed!` : `Running...`) : `✗ Step ${i+1} failed — fix and retry`);
3973
+ }, i, result, steps.length).catch(()=>{});
3974
+ if (!result.passed) break;
3975
+ await new Promise(r => setTimeout(r, 400));
3976
+ }
3977
+ });
3978
+
3979
+ await ctx.exposeFunction('__skopixStopPreview', async () => {
3980
+ try { await browser.close(); } catch {}
3981
+ stepTesterSessions.delete(testerId);
3982
+ });
3983
+
3984
+ // THEN inject toolbar via addInitScript
3955
3985
  // PREVIEW MODE — inject steps list toolbar
3956
3986
  await ctx.addInitScript((stepsData) => {
3957
3987
  window.__skopixPreviewSteps = stepsData;
@@ -4059,37 +4089,38 @@ async function startStepTester(testerId, url, selector, mode, steps) {
4059
4089
  });
4060
4090
  }, steps.map(s => ({ action: s.action, selector: s.stableSelector||s.selector||'', description: s.description||s.action, value: s.value||'' })));
4061
4091
 
4062
- // Expose run functions
4063
- await ctx.exposeFunction('__skopixPreviewRun', async ({ index }) => {
4064
- const s = steps[index];
4065
- if (!s) return;
4066
- await page.evaluate((i, status) => { if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, null, status); }, index, `Running step ${index+1}/${steps.length}...`);
4067
- const result = await executeStepTesterAction(page, { selector: s.stableSelector||s.selector, action: s.action, value: s.value||'', assertType: s.assertType });
4068
- await page.evaluate((i, r, total) => {
4069
- if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, r, r.passed ? (i+1 >= total ? `✓ All ${total} steps passed!` : `✓ Step ${i+1} passed`) : `✗ Step ${i+1} failed`);
4070
- }, index, result, steps.length);
4071
- });
4072
-
4073
- await ctx.exposeFunction('__skopixPreviewRunAll', async ({ fromIndex }) => {
4074
- for (let i = fromIndex; i < steps.length; i++) {
4075
- const s = steps[i];
4076
- await page.evaluate((i, total) => { if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, null, `Running step ${i+1}/${total}...`); }, i, steps.length);
4077
- const result = await executeStepTesterAction(page, { selector: s.stableSelector||s.selector, action: s.action, value: s.value||'', assertType: s.assertType });
4078
- await page.evaluate((i, r, total) => {
4079
- if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, r, r.passed ? (i+1 >= total ? `✓ All ${total} steps passed!` : `Running...`) : `✗ Step ${i+1} failed — fix and retry`);
4080
- }, i, result, steps.length);
4081
- if (!result.passed) break;
4082
- await new Promise(r => setTimeout(r, 400));
4083
- }
4092
+ } else {
4093
+ // STEP TESTER MODE register expose functions FIRST
4094
+ await ctx.exposeFunction('__skopixTesterRun', async ({ sel, action, value }) => {
4095
+ const result = await executeStepTesterAction(page, { selector: sel, action, value });
4096
+ await page.evaluate((passed, errMsg) => {
4097
+ const el = document.getElementById('__skopix_result');
4098
+ if (el) { el.textContent = passed ? '✓' : '✗'; el.style.color = passed ? '#34d399' : '#ef4444'; }
4099
+ }, result.passed, result.error || '').catch(async () => {
4100
+ await new Promise(r => setTimeout(r, 500));
4101
+ await page.evaluate((passed) => {
4102
+ const el = document.getElementById('__skopix_result');
4103
+ if (el) { el.textContent = passed ? '' : '✗'; el.style.color = passed ? '#34d399' : '#ef4444'; }
4104
+ }, result.passed).catch(() => {});
4105
+ });
4106
+ await page.evaluate((passed, sel) => {
4107
+ try {
4108
+ const target = document.querySelector(sel);
4109
+ if (target) {
4110
+ const orig = target.style.outline;
4111
+ target.style.outline = passed ? '3px solid #34d399' : '3px solid #ef4444';
4112
+ setTimeout(() => { target.style.outline = orig; }, 1500);
4113
+ }
4114
+ } catch {}
4115
+ }, result.passed, sel).catch(() => {});
4084
4116
  });
4085
4117
 
4086
- await ctx.exposeFunction('__skopixStopPreview', async () => {
4118
+ await ctx.exposeFunction('__skopixTesterStop', async () => {
4087
4119
  try { await browser.close(); } catch {}
4088
4120
  stepTesterSessions.delete(testerId);
4089
4121
  });
4090
4122
 
4091
- } else {
4092
- // STEP TESTER MODE — single step toolbar
4123
+ // THEN inject toolbar
4093
4124
  await ctx.addInitScript((sel) => {
4094
4125
  window.__skopixTesterSelector = sel;
4095
4126
  document.addEventListener('DOMContentLoaded', () => {
@@ -4146,41 +4177,6 @@ async function startStepTester(testerId, url, selector, mode, steps) {
4146
4177
  });
4147
4178
  });
4148
4179
  }, selector || '');
4149
-
4150
- await ctx.exposeFunction('__skopixTesterRun', async ({ sel, action, value }) => {
4151
- const result = await executeStepTesterAction(page, { selector: sel, action, value });
4152
- // Update toolbar via exposeFunction callback — more reliable than evaluate
4153
- await page.evaluate((passed, errMsg) => {
4154
- const el = document.getElementById('__skopix_result');
4155
- if (el) {
4156
- el.textContent = passed ? '✓' : '✗';
4157
- el.style.color = passed ? '#34d399' : '#ef4444';
4158
- }
4159
- }, result.passed, result.error || '').catch(async () => {
4160
- // If evaluate fails (e.g. page navigated), wait and retry once
4161
- await new Promise(r => setTimeout(r, 500));
4162
- await page.evaluate((passed) => {
4163
- const el = document.getElementById('__skopix_result');
4164
- if (el) { el.textContent = passed ? '✓' : '✗'; el.style.color = passed ? '#34d399' : '#ef4444'; }
4165
- }, result.passed).catch(() => {});
4166
- });
4167
- // Highlight element
4168
- await page.evaluate((passed, sel) => {
4169
- try {
4170
- const target = document.querySelector(sel);
4171
- if (target) {
4172
- const orig = target.style.outline;
4173
- target.style.outline = passed ? '3px solid #34d399' : '3px solid #ef4444';
4174
- setTimeout(() => { target.style.outline = orig; }, 1500);
4175
- }
4176
- } catch {}
4177
- }, result.passed, sel).catch(() => {});
4178
- });
4179
-
4180
- await ctx.exposeFunction('__skopixTesterStop', async () => {
4181
- try { await browser.close(); } catch {}
4182
- stepTesterSessions.delete(testerId);
4183
- });
4184
4180
  }
4185
4181
 
4186
4182
  if (url) await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 }).catch(() => {});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skopix",
3
- "version": "2.0.60",
3
+ "version": "2.0.61",
4
4
  "description": "Browser-based QA tool — record tests by using your app, replay them deterministically, generate Playwright code automatically",
5
5
  "main": "cli/index.js",
6
6
  "bin": {