import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { loadGeneratedWorkspace, loadGeneratedSelectors } from '../support/qkforce-generated.mjs'; import { createQkfActions } from '../support/qkforce-actions.mjs'; function fixture(t, features=['test/qkforce/features/project.feature']) { const root=fs.mkdtempSync(path.join(os.tmpdir(),'qkf-generated-')); t.after(()=>fs.rmSync(root,{recursive:true,force:true})); fs.mkdirSync(path.join(root,'test/qkforce/features'),{recursive:true}); fs.writeFileSync(path.join(root,'test/qkforce/features/project.feature'),'Feature: project'); fs.writeFileSync(path.join(root,'test/qkforce/steps.mjs'),''); fs.writeFileSync(path.join(root,'test/qkforce/selectors.json'),'{}'); const manifest={schemaVersion:1,features,steps:'test/qkforce/steps.mjs',selectors:'test/qkforce/selectors.json'}; fs.writeFileSync(path.join(root,'test/qkforce/manifest.json'),JSON.stringify(manifest)); return root; } test('runs exactly generated cases and never template or stale feature globs', t=>{ const root=fixture(t); fs.writeFileSync(path.join(root,'test/qkforce/features/stale.feature'),'Feature: stale'); assert.deepEqual(loadGeneratedWorkspace(root),{specs:['./test/qkforce/features/project.feature'],imports:['./test/qkforce/steps.mjs']}); assert.deepEqual(loadGeneratedSelectors(root),{}); }); test('an empty generated selection fails instead of running the template sample',t=>{ assert.throws(()=>loadGeneratedWorkspace(fixture(t,[])),/at least one/); }); test('an API package without generated selection cannot fall back to samples',t=>{ const root=fixture(t);fs.unlinkSync(path.join(root,'test/qkforce/manifest.json')); assert.equal(loadGeneratedWorkspace(root),null); fs.writeFileSync(path.join(root,'qkforce-package-manifest.json'),'{}'); assert.throws(()=>loadGeneratedWorkspace(root),/regenerate/); }); test('unsafe, missing, duplicate and symlinked feature inputs are rejected',t=>{ for(const features of [['test/qkforce/features/../../secret.feature'],['test/qkforce/features/missing.feature'],['test/qkforce/features/project.feature','test/qkforce/features/project.feature']]) { assert.throws(()=>loadGeneratedWorkspace(fixture(t,features))); } const root=fixture(t); const feature=path.join(root,'test/qkforce/features/project.feature'); fs.unlinkSync(feature);fs.symlinkSync(path.join(root,'test/qkforce/steps.mjs'),feature); assert.throws(()=>loadGeneratedWorkspace(root),/symlinks/); }); test('legacy unawaited actions execute sequentially and finish before flush returns',async()=>{ const events=[]; const browser={url:async url=>{await Promise.resolve();events.push(url);},$:async selector=>({click:async()=>events.push(selector)})}; const qkf=createQkfActions(browser); qkf.goto('/login');qkf.click('#submit'); await qkf.flush();assert.deepEqual(events,['/login','#submit']); }); test('queued action failures fail the step and reset for the next scenario',async()=>{ let fail=true; const qkf=createQkfActions({url:async()=>{if(fail)throw new Error('navigation failed');}}); qkf.goto('/login');await assert.rejects(qkf.flush(),/navigation failed/); fail=false;qkf.goto('/ok');await qkf.flush(); }); test('all palette element actions map to real WDIO methods',async()=>{ const calls=[]; const element=new Proxy({}, {get:(_,method)=>method === 'then' ? undefined : (...args)=>{calls.push([method,...args]);return true;}}); const qkf=createQkfActions({$:()=>element}); for(const method of ['click','doubleClick','rightClick','enterValue','addValue','clearValue','isVisible','exists','isEnabled','isClickable','isSelected','waitForVisible','waitForExistence','waitForClickable','waitForEnabled']) qkf[method]('#x','value'); await qkf.flush(); assert.equal(calls.length,15);assert.deepEqual(calls[2],['click',{button:'right'}]);assert.deepEqual(calls[3],['setValue','value']); }); test('screenshots stay inside the results directory',async t=>{ const root=fixture(t);let saved; const qkf=createQkfActions({saveScreenshot:async file=>{saved=file;}},root); qkf.takeScreenshot('../../outside');await qkf.flush(); assert.equal(path.dirname(saved),path.join(root,'qreport-results/screenshots')); });