skopix 2.0.60 → 2.0.62

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,38 @@ 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); }, { i: index, status: `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
+ const msg = result.passed ? (index+1 >= steps.length ? `✓ All ${steps.length} steps passed!` : `✓ Step ${index+1} passed`) : `✗ Step ${index+1} failed`;
3962
+ await page.evaluate(({ i, r, msg }) => {
3963
+ if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, r, msg);
3964
+ }, { i: index, r: { passed: result.passed, error: result.error||null }, msg }).catch(()=>{});
3965
+ });
3966
+
3967
+ await ctx.exposeFunction('__skopixPreviewRunAll', async ({ fromIndex }) => {
3968
+ for (let i = fromIndex; i < steps.length; i++) {
3969
+ const s = steps[i];
3970
+ await page.evaluate(({ i, total }) => { if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, null, `Running step ${i+1}/${total}...`); }, { i, total: steps.length }).catch(()=>{});
3971
+ const result = await executeStepTesterAction(page, { selector: s.stableSelector||s.selector, action: s.action, value: s.value||'', assertType: s.assertType });
3972
+ const msg = result.passed ? (i+1 >= steps.length ? `✓ All ${steps.length} steps passed!` : `Running...`) : `✗ Step ${i+1} failed — fix and retry`;
3973
+ await page.evaluate(({ i, r, msg }) => {
3974
+ if(window.__skopixUpdatePreview) window.__skopixUpdatePreview(i, r, msg);
3975
+ }, { i, r: { passed: result.passed, error: result.error||null }, msg }).catch(()=>{});
3976
+ if (!result.passed) break;
3977
+ await new Promise(r => setTimeout(r, 400));
3978
+ }
3979
+ });
3980
+
3981
+ await ctx.exposeFunction('__skopixStopPreview', async () => {
3982
+ try { await browser.close(); } catch {}
3983
+ stepTesterSessions.delete(testerId);
3984
+ });
3985
+
3986
+ // THEN inject toolbar via addInitScript
3955
3987
  // PREVIEW MODE — inject steps list toolbar
3956
3988
  await ctx.addInitScript((stepsData) => {
3957
3989
  window.__skopixPreviewSteps = stepsData;
@@ -4059,37 +4091,38 @@ async function startStepTester(testerId, url, selector, mode, steps) {
4059
4091
  });
4060
4092
  }, steps.map(s => ({ action: s.action, selector: s.stableSelector||s.selector||'', description: s.description||s.action, value: s.value||'' })));
4061
4093
 
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
- }
4094
+ } else {
4095
+ // STEP TESTER MODE register expose functions FIRST
4096
+ await ctx.exposeFunction('__skopixTesterRun', async ({ sel, action, value }) => {
4097
+ const result = await executeStepTesterAction(page, { selector: sel, action, value });
4098
+ await page.evaluate(({ passed }) => {
4099
+ const el = document.getElementById('__skopix_result');
4100
+ if (el) { el.textContent = passed ? '✓' : '✗'; el.style.color = passed ? '#34d399' : '#ef4444'; }
4101
+ }, { passed: result.passed }).catch(async () => {
4102
+ await new Promise(r => setTimeout(r, 500));
4103
+ await page.evaluate(({ passed }) => {
4104
+ const el = document.getElementById('__skopix_result');
4105
+ if (el) { el.textContent = passed ? '' : '✗'; el.style.color = passed ? '#34d399' : '#ef4444'; }
4106
+ }, { passed: result.passed }).catch(() => {});
4107
+ });
4108
+ await page.evaluate(({ passed, sel }) => {
4109
+ try {
4110
+ const target = document.querySelector(sel);
4111
+ if (target) {
4112
+ const orig = target.style.outline;
4113
+ target.style.outline = passed ? '3px solid #34d399' : '3px solid #ef4444';
4114
+ setTimeout(() => { target.style.outline = orig; }, 1500);
4115
+ }
4116
+ } catch {}
4117
+ }, { passed: result.passed, sel }).catch(() => {});
4084
4118
  });
4085
4119
 
4086
- await ctx.exposeFunction('__skopixStopPreview', async () => {
4120
+ await ctx.exposeFunction('__skopixTesterStop', async () => {
4087
4121
  try { await browser.close(); } catch {}
4088
4122
  stepTesterSessions.delete(testerId);
4089
4123
  });
4090
4124
 
4091
- } else {
4092
- // STEP TESTER MODE — single step toolbar
4125
+ // THEN inject toolbar
4093
4126
  await ctx.addInitScript((sel) => {
4094
4127
  window.__skopixTesterSelector = sel;
4095
4128
  document.addEventListener('DOMContentLoaded', () => {
@@ -4146,41 +4179,6 @@ async function startStepTester(testerId, url, selector, mode, steps) {
4146
4179
  });
4147
4180
  });
4148
4181
  }, 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
4182
  }
4185
4183
 
4186
4184
  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.62",
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": {