{"_id":"@civitas-cerebrum/singularity-engine","_rev":"7-4e85937ac81c40f87dbb355767611ac3","name":"@civitas-cerebrum/singularity-engine","dist-tags":{"latest":"0.1.6"},"versions":{"0.1.0":{"name":"@civitas-cerebrum/singularity-engine","version":"0.1.0","keywords":["playwright","automation","interactions","assertions","clean-code","facade-pattern","test-automation","steps-api"],"author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/singularity-engine@0.1.0","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/singularity-engine#readme","bugs":{"url":"https://github.com/civitas-cerebrum/singularity-engine/issues"},"dist":{"shasum":"432f4031600395697651f13921f3b8128425e1ab","tarball":"https://registry.npmjs.org/@civitas-cerebrum/singularity-engine/-/singularity-engine-0.1.0.tgz","fileCount":70,"integrity":"sha512-k3erK7BCdxaxer7HGXHZKmDyW+dVR276ZnjlgL7Wn7ju5E7or5RqJFjPCFODnQPAUUKVHKVxpICQCw4REnY0Fg==","signatures":[{"sig":"MEUCIQCSEqObMzTuAalyjwYv/INt6RLOrB5vsUeskFRs29KbMgIgaw4K9vV/dAxyKQ2H4J7Y6CkKqKvfQPbBIyiFhKrK8/0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":262724},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"f902545c4dc94081ab0b2fa6a3b9b736b9811e98","scripts":{"test":"npm run test:unit","build":"npm run clean && tsc && npm run obfuscate","clean":"rm -rf dist","test:ui":"npx playwright test --headed","test:all":"npm run test:unit && npm run test:email","obfuscate":"javascript-obfuscator dist --output dist --config .obfuscatorrc.json --exclude 'dist/**/*.d.ts'","test:unit":"npx playwright test --grep-invert 'Email Integration Tests'","test:email":"npx playwright test --grep 'Email Integration Tests'","test:quiet":"TESTER_DEBUG=false npx playwright test","test:coverage":"npx test-coverage","prepublishOnly":"npm run build","test:with-coverage":"npx playwright test --grep-invert 'Email Integration Tests' && npx test-coverage --format github-plain"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"repository":{"url":"git+https://github.com/civitas-cerebrum/singularity-engine.git","type":"git"},"_npmVersion":"11.11.0","description":"Platform-agnostic test automation engine. Semantic Steps API for web, mobile, and API testing.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","dotenv":"^17.3.1","webdriverio":"^9.27.0","@civitas-cerebrum/wasapi":"^0.0.3","@civitas-cerebrum/email-client":"^0.0.6","@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/element-repository":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13","@playwright/test":"^1.58.2","javascript-obfuscator":"^5.4.1","@civitas-cerebrum/test-coverage":"^0.0.9"},"peerDependencies":{"@playwright/test":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/singularity-engine_0.1.0_1775510194797_0.7806873924350395","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@civitas-cerebrum/singularity-engine","version":"0.1.1","keywords":["playwright","automation","interactions","assertions","clean-code","facade-pattern","test-automation","steps-api"],"author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/singularity-engine@0.1.1","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/singularity-engine#readme","bugs":{"url":"https://github.com/civitas-cerebrum/singularity-engine/issues"},"dist":{"shasum":"1760e42eb64ac9a6b377f253cf92fa811f8e7c7b","tarball":"https://registry.npmjs.org/@civitas-cerebrum/singularity-engine/-/singularity-engine-0.1.1.tgz","fileCount":70,"integrity":"sha512-WYT1goJELSJgvquW6EPYyyTj0yTexRgFD1+aGo4vamBTRdZ373YdvyCh6Mik2DHGpS2XgFThaK+lENCwK2it5g==","signatures":[{"sig":"MEUCIQCIWGdmqU73JgYeejIjSIuyrdqxgjk2iI94n1YSZB3DhAIge6cI2Nqvd5fCWlQXZK+bxpNCLnWH/aILhRxucQc9UGk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":262554},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"650b4e3166b4d908acd2aa908f5352e9613bb8c2","scripts":{"test":"npm run test:unit","build":"npm run clean && tsc && npm run obfuscate","clean":"rm -rf dist","test:ui":"npx playwright test --headed","test:all":"npm run test:unit && npm run test:email","obfuscate":"javascript-obfuscator dist --output dist --config .obfuscatorrc.json --exclude 'dist/**/*.d.ts'","test:unit":"npx playwright test --grep-invert 'Email Integration Tests'","test:email":"npx playwright test --grep 'Email Integration Tests'","test:quiet":"TESTER_DEBUG=false npx playwright test","test:coverage":"npx test-coverage","prepublishOnly":"npm run build","test:with-coverage":"npx playwright test --grep-invert 'Email Integration Tests' && npx test-coverage --format github-plain"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"repository":{"url":"git+https://github.com/civitas-cerebrum/singularity-engine.git","type":"git"},"_npmVersion":"11.11.0","description":"Platform-agnostic test automation engine. Semantic Steps API for web, mobile, and API testing.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","dotenv":"^17.3.1","webdriverio":"^9.27.0","@civitas-cerebrum/wasapi":"^0.0.3","@civitas-cerebrum/email-client":"^0.0.6","@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/element-repository":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13","@playwright/test":"^1.58.2","javascript-obfuscator":"^5.4.1","@civitas-cerebrum/test-coverage":"^0.0.9"},"peerDependencies":{"@playwright/test":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/singularity-engine_0.1.1_1775539556126_0.4188026254553734","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@civitas-cerebrum/singularity-engine","version":"0.1.2","keywords":["playwright","automation","interactions","assertions","clean-code","facade-pattern","test-automation","steps-api"],"author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/singularity-engine@0.1.2","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/singularity-engine#readme","bugs":{"url":"https://github.com/civitas-cerebrum/singularity-engine/issues"},"dist":{"shasum":"8bfbc695191e1a04bece5a274581e768b20faf2b","tarball":"https://registry.npmjs.org/@civitas-cerebrum/singularity-engine/-/singularity-engine-0.1.2.tgz","fileCount":70,"integrity":"sha512-2rkj84mXbMRcH1W54xBkzyx8PqzL06/2pYDgBJIXI7Do4zyqyurAMDKSb8XW99r8SXDMqmzQuqSsjvq0hixH0A==","signatures":[{"sig":"MEUCIGZGgCfB3J+/2O4RLJFG3XyIUsneAp727SrzoGdBFBAtAiEAwpy56ugNMr/pGF3462P/BbbrLPFmK3nKjZk8IgNB7KQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":257380},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"871e9b95bed0546aae49adb2f7c00adbacbb3860","scripts":{"test":"npm run test:unit","build":"npm run clean && tsc && npm run obfuscate","clean":"rm -rf dist","test:ui":"npx playwright test --headed","test:all":"npm run test:unit && npm run test:email","obfuscate":"node scripts/obfuscate.js","test:unit":"npx playwright test --grep-invert 'Email Integration Tests'","test:email":"npx playwright test --grep 'Email Integration Tests'","test:quiet":"TESTER_DEBUG=false npx playwright test","test:coverage":"npx test-coverage","prepublishOnly":"npm run build","test:with-coverage":"npx playwright test --grep-invert 'Email Integration Tests' && npx test-coverage --format github-plain"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"repository":{"url":"git+https://github.com/civitas-cerebrum/singularity-engine.git","type":"git"},"_npmVersion":"11.11.0","description":"Platform-agnostic test automation engine. Semantic Steps API for web, mobile, and API testing.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","dotenv":"^17.3.1","webdriverio":"^9.27.0","@civitas-cerebrum/wasapi":"^0.0.3","@civitas-cerebrum/email-client":"^0.0.6","@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/element-repository":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13","@playwright/test":"^1.58.2","javascript-obfuscator":"^5.4.1","@civitas-cerebrum/test-coverage":"^0.0.9"},"peerDependencies":{"@playwright/test":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/singularity-engine_0.1.2_1775543287987_0.23970724000965737","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@civitas-cerebrum/singularity-engine","version":"0.1.3","keywords":["playwright","automation","interactions","assertions","clean-code","facade-pattern","test-automation","steps-api"],"author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/singularity-engine@0.1.3","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/singularity-engine#readme","bugs":{"url":"https://github.com/civitas-cerebrum/singularity-engine/issues"},"dist":{"shasum":"c75086b614ea50510beb11aef2bb0da6849cb007","tarball":"https://registry.npmjs.org/@civitas-cerebrum/singularity-engine/-/singularity-engine-0.1.3.tgz","fileCount":72,"integrity":"sha512-F5BEFBzK5czrk9n9D4KtppuLBf4QgNb17oE/fVL9iakj6MMVWwF5fNQ4quvjShggtOqNYcWhannD3SqR3zTSwA==","signatures":[{"sig":"MEQCIDW+SrJ1PFH6NzD0AXUG4gvIp8b95zmBCb9Rys+FJ+pvAiBPOS28tcljYch19ql/UtrmaPcUTrobXVM632YHrJ8+ZQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":249069},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"a91a7202725ccbcd4e5fdf36ddf020c6ce306cfb","scripts":{"test":"npm run test:unit","build":"npm run clean && tsc && npm run obfuscate","clean":"rm -rf dist","test:ui":"npx playwright test --headed","test:all":"npm run test:unit && npm run test:email","obfuscate":"node scripts/obfuscate.js","test:unit":"npx playwright test --grep-invert 'Email Integration Tests'","test:email":"npx playwright test --grep 'Email Integration Tests'","test:quiet":"TESTER_DEBUG=false npx playwright test","test:coverage":"npx test-coverage","prepublishOnly":"npm run build","test:with-coverage":"npx playwright test --grep-invert 'Email Integration Tests' && npx test-coverage --format github-plain"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"repository":{"url":"git+https://github.com/civitas-cerebrum/singularity-engine.git","type":"git"},"_npmVersion":"11.11.0","description":"Platform-agnostic test automation engine. Semantic Steps API for web, mobile, and API testing.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","dotenv":"^17.3.1","webdriverio":"^9.27.0","@civitas-cerebrum/wasapi":"^0.0.3","@civitas-cerebrum/email-client":"^0.0.6","@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/element-repository":"^0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13","@playwright/test":"^1.58.2","javascript-obfuscator":"^5.4.1","@civitas-cerebrum/test-coverage":"^0.0.9"},"peerDependencies":{"@playwright/test":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/singularity-engine_0.1.3_1775837920722_0.039847379295084195","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@civitas-cerebrum/singularity-engine","version":"0.1.4","keywords":["playwright","automation","interactions","assertions","clean-code","facade-pattern","test-automation","steps-api"],"author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/singularity-engine@0.1.4","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/singularity-engine#readme","bugs":{"url":"https://github.com/civitas-cerebrum/singularity-engine/issues"},"dist":{"shasum":"b6a05478db1a833cc34da8ed893d1304b445fd7b","tarball":"https://registry.npmjs.org/@civitas-cerebrum/singularity-engine/-/singularity-engine-0.1.4.tgz","fileCount":72,"integrity":"sha512-Ghj2S5HBD2K6EuJ1yS6JJOgjnZ8skfLGcz/mz67+K8lsxUcyR6gXuFhka1S7CJiBTsC6ZgS1GhPcX4TQvtzz5A==","signatures":[{"sig":"MEUCIQC6yfkdabhET9LtwS8U8sAjEPHUyzLXpnmyF1Vkr3SgYQIgXl41/Rpw77+ff4UOj8dk7gfDnoYB73LdVQScouELh3Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":252020},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"a47a2a721995d9da4caaa96a6c89ea7d4ae22cf1","scripts":{"test":"npm run test:unit","build":"npm run clean && tsc && npm run obfuscate","clean":"rm -rf dist","test:ui":"npx playwright test --headed","test:all":"npm run test:unit && npm run test:email","obfuscate":"node scripts/obfuscate.js","test:unit":"npx playwright test --grep-invert 'Email Integration Tests'","test:email":"npx playwright test --grep 'Email Integration Tests'","test:quiet":"TESTER_DEBUG=false npx playwright test","test:coverage":"npx test-coverage","prepublishOnly":"npm run build","test:with-coverage":"npx playwright test --grep-invert 'Email Integration Tests' && npx test-coverage --format github-plain"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"repository":{"url":"git+https://github.com/civitas-cerebrum/singularity-engine.git","type":"git"},"_npmVersion":"11.11.0","description":"Platform-agnostic test automation engine. Semantic Steps API for web, mobile, and API testing.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","dotenv":"^17.3.1","webdriverio":"^9.27.0","@civitas-cerebrum/wasapi":"^0.0.3","@civitas-cerebrum/email-client":"^0.0.6","@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/element-repository":"^0.1.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13","@playwright/test":"^1.58.2","javascript-obfuscator":"^5.4.1","@civitas-cerebrum/test-coverage":"^0.0.9"},"peerDependencies":{"@playwright/test":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/singularity-engine_0.1.4_1775845664553_0.8411582766211245","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@civitas-cerebrum/singularity-engine","version":"0.1.5","keywords":["playwright","automation","interactions","assertions","clean-code","facade-pattern","test-automation","steps-api"],"author":{"name":"Umut Ay Bora"},"license":"MIT","_id":"@civitas-cerebrum/singularity-engine@0.1.5","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"homepage":"https://github.com/civitas-cerebrum/singularity-engine#readme","bugs":{"url":"https://github.com/civitas-cerebrum/singularity-engine/issues"},"dist":{"shasum":"be59c5c4f9d9ede966317040e670665606456ec1","tarball":"https://registry.npmjs.org/@civitas-cerebrum/singularity-engine/-/singularity-engine-0.1.5.tgz","fileCount":78,"integrity":"sha512-khkaVxqxRovcHweh99pXyWbQJGxktcEfrINNldVn0d+Yb0b52Kl3GHzpzRr4Tj0tK2IxPLBDgToajXqUQtamug==","signatures":[{"sig":"MEUCIA7JkqOFeoCokByjIzy1J9faYcEcwTSiKxV2ae5pjjLLAiEAhuWInHsCSnX0VBVKnmRjqYxjJyqw3QKvxu8cYZdXlYQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":366627},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"3de5586dad6ea8e47ff52f4b8f46551190e19025","scripts":{"test":"npm run test:unit","build":"npm run clean && tsc && npm run obfuscate","clean":"rm -rf dist","test:ui":"npx playwright test --headed","test:all":"npm run test:unit && npm run test:email","obfuscate":"node scripts/obfuscate.js","test:unit":"npx playwright test --grep-invert 'Email Integration Tests'","test:email":"npx playwright test --grep 'Email Integration Tests'","test:quiet":"TESTER_DEBUG=false npx playwright test","test:mobile":"npx playwright test --config=playwright.mobile.config.ts","test:coverage":"npx test-coverage","prepublishOnly":"npm run build","test:mobile:ios":"MOBILE_PLATFORM=ios npx playwright test --config=playwright.mobile.config.ts","test:with-coverage":"npx playwright test --grep-invert 'Email Integration Tests' && npx test-coverage --format github-plain","test:mobile:android":"MOBILE_PLATFORM=android npx playwright test --config=playwright.mobile.config.ts"},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"overrides":{"basic-ftp":"^5.3.0","nodemailer":"^8.0.5"},"repository":{"url":"git+https://github.com/civitas-cerebrum/singularity-engine.git","type":"git"},"_npmVersion":"11.11.0","description":"Platform-agnostic test automation engine. Semantic Steps API for web, mobile, and API testing.","directories":{},"_nodeVersion":"24.14.1","dependencies":{"debug":"^4.4.3","dotenv":"^17.3.1","webdriverio":"^9.27.0","@civitas-cerebrum/wasapi":"^0.0.3","@civitas-cerebrum/email-client":"^0.0.6","@civitas-cerebrum/context-store":"^0.0.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","@types/node":"^20.0.0","@types/debug":"^4.1.13","@playwright/test":"^1.58.2","javascript-obfuscator":"^5.4.1","@civitas-cerebrum/test-coverage":"^0.0.9","@civitas-cerebrum/element-repository":"^0.1.8"},"peerDependencies":{"@playwright/test":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/singularity-engine_0.1.5_1776629806869_0.6909950881977627","host":"s3://npm-registry-packages-npm-production"}},"0.1.6":{"name":"@civitas-cerebrum/singularity-engine","version":"0.1.6","description":"Platform-agnostic test automation engine. Semantic Steps API for web, mobile, and API testing.","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"clean":"rm -rf dist","obfuscate":"node scripts/obfuscate.js","build":"npm run clean && tsc && npm run obfuscate","test":"npm run test:unit","test:all":"npm run test:unit && npm run test:email","test:unit":"npx playwright test --grep-invert 'Email Integration Tests'","test:email":"npx playwright test --grep 'Email Integration Tests'","test:ui":"npx playwright test --headed","prepublishOnly":"npm run build","test:quiet":"TESTER_DEBUG=false npx playwright test","test:mobile":"npx playwright test --config=playwright.mobile.config.ts","test:mobile:android":"MOBILE_PLATFORM=android npx playwright test --config=playwright.mobile.config.ts","test:mobile:ios":"MOBILE_PLATFORM=ios npx playwright test --config=playwright.mobile.config.ts","test:coverage":"npx test-coverage","test:with-coverage":"npx playwright test --grep-invert 'Email Integration Tests' && npx test-coverage --format github-plain"},"peerDependencies":{"@playwright/test":">=1.5.0"},"devDependencies":{"@civitas-cerebrum/test-coverage":"^0.1.0","@playwright/test":"^1.58.2","@types/debug":"^4.1.13","@types/node":"^20.0.0","javascript-obfuscator":"^5.4.1","typescript":"^5.0.0"},"keywords":["playwright","automation","interactions","assertions","clean-code","facade-pattern","test-automation","steps-api"],"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/civitas-cerebrum/singularity-engine.git"},"author":{"name":"Umut Ay Bora"},"license":"MIT","dependencies":{"@civitas-cerebrum/context-store":"^0.0.2","@civitas-cerebrum/element-repository":"^0.1.8","@civitas-cerebrum/email-client":"^0.0.7","@civitas-cerebrum/wasapi":"^0.0.3","debug":"^4.4.3","dotenv":"^17.3.1","webdriverio":"^9.27.0"},"overrides":{"basic-ftp":"^5.3.0","nodemailer":"^8.0.5"},"gitHead":"4bffb8e03505e1de69a80b50c6131fd1c624b02f","_id":"@civitas-cerebrum/singularity-engine@0.1.6","bugs":{"url":"https://github.com/civitas-cerebrum/singularity-engine/issues"},"homepage":"https://github.com/civitas-cerebrum/singularity-engine#readme","_nodeVersion":"24.14.1","_npmVersion":"11.11.0","dist":{"integrity":"sha512-xk4OmM54jn+CwQ/3lmc+MjakeB7mMY7vBy4cjY87nBxBJBWX7LU7SKN+StaUvzRu+kuVK5mMTcgcRHf+eHn55A==","shasum":"3a415b7fba295ffafedf3f0292934d169d384559","tarball":"https://registry.npmjs.org/@civitas-cerebrum/singularity-engine/-/singularity-engine-0.1.6.tgz","fileCount":78,"unpackedSize":365851,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIC1qPGie3jRfoUoEdQNjSX5HlDoOOtyU9kyFK/wwMXMCAiEA9zi0CCkq5q6ECzVHmTEEgFkeT2FPrJ9lahzKpfx7780="}]},"_npmUser":{"name":"umutayb","email":"umutaybora@gmail.com"},"directories":{},"maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/singularity-engine_0.1.6_1776631557971_0.6075325039783432"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-06T21:16:34.678Z","modified":"2026-04-19T20:45:58.251Z","0.1.0":"2026-04-06T21:16:34.941Z","0.1.1":"2026-04-07T05:25:56.256Z","0.1.2":"2026-04-07T06:28:08.218Z","0.1.3":"2026-04-10T16:18:40.917Z","0.1.4":"2026-04-10T18:27:44.696Z","0.1.5":"2026-04-19T20:16:47.030Z","0.1.6":"2026-04-19T20:45:58.105Z"},"bugs":{"url":"https://github.com/civitas-cerebrum/singularity-engine/issues"},"author":{"name":"Umut Ay Bora"},"license":"MIT","homepage":"https://github.com/civitas-cerebrum/singularity-engine#readme","keywords":["playwright","automation","interactions","assertions","clean-code","facade-pattern","test-automation","steps-api"],"repository":{"type":"git","url":"git+https://github.com/civitas-cerebrum/singularity-engine.git"},"description":"Platform-agnostic test automation engine. Semantic Steps API for web, mobile, and API testing.","maintainers":[{"name":"umutayb","email":"umutaybora@gmail.com"}],"readme":"# Singularity Engine\n\n[![NPM Version](https://img.shields.io/npm/v/@civitas-cerebrum/singularity-engine?color=rgb(88%2C%20171%2C%2070))](https://www.npmjs.com/package/@civitas-cerebrum/singularity-engine)\n\nA platform-agnostic test automation engine. Write once, run on web, mobile, and desktop.\n\n`@civitas-cerebrum/singularity-engine` pairs with `@civitas-cerebrum/element-repository` to achieve a fully decoupled test automation architecture. By separating **Element Acquisition** from **Element Interaction** behind an abstract factory pattern, the same test steps work across Playwright (web), Appium (Android, iOS), and desktop apps (Windows, Mac) — without changing test code.\n\n### ✨ The Unified Steps API\n\nThe `Steps` class provides a single, semantic API that works identically across platforms. Under the hood, an `IInteractionFactory` routes each call to the correct platform implementation.\n\n```ts\n// This test runs on web, Android, and iOS — no code changes needed\nawait steps.navigateTo('/');\nawait steps.click('addToCartButton', 'HomePage');\nawait steps.verifyText('total', 'CartPage', '$29.99');\n\n// API testing built-in\nconst orders = await steps.apiGet<Order[]>('/api/orders');\nawait steps.verifyApiStatus(orders, 200);\n```\n\n### 🧠 AI-Friendly Test Development\n\nThe API is highly semantic and completely decoupled from the DOM and platform internals, making it an **ideal framework for AI coding assistants**. AI models generate robust test flows using plain-English strings (`'CheckoutPage'`, `'submitButton'`) without hallucinating complex selectors.\n\n---\n\n## 📦 Installation\n\n```bash\nnpm install @civitas-cerebrum/singularity-engine\n```\n\n**Peer dependency:** `@playwright/test` is required for web testing. Install it alongside:\n\n```bash\nnpm install @civitas-cerebrum/singularity-engine @playwright/test\n```\n\n---\n\n## 🚀 Quick Start\n\n### 1. Create the test fixture\n\n```ts\n// tests/fixtures/base.ts\nimport { test as base, expect } from '@playwright/test';\nimport { baseFixture } from '@civitas-cerebrum/singularity-engine';\n\nexport const test = baseFixture(base, 'tests/data/page-repository.json');\nexport { expect };\n```\n\n### 2. Define your selectors\n\n```json\n// tests/data/page-repository.json\n{\n  \"pages\": [\n    {\n      \"name\": \"HomePage\",\n      \"elements\": [\n        { \"elementName\": \"searchInput\", \"selector\": { \"css\": \"[data-testid='search']\" } },\n        { \"elementName\": \"addToCartButton\", \"selector\": { \"css\": \"[data-testid='add-to-cart']\" } },\n        { \"elementName\": \"bookCard\", \"selector\": { \"css\": \"[data-testid^='book-card-']\" } }\n      ]\n    },\n    {\n      \"name\": \"CartPage\",\n      \"elements\": [\n        { \"elementName\": \"total\", \"selector\": { \"css\": \"[data-testid='cart-total']\" } },\n        { \"elementName\": \"checkoutButton\", \"selector\": { \"css\": \"[data-testid='checkout-btn']\" } },\n        { \"elementName\": \"emptyMessage\", \"selector\": { \"css\": \"[data-testid='cart-empty']\" } }\n      ]\n    }\n  ]\n}\n```\n\n### 3. Write tests\n\n```ts\n// tests/checkout.spec.ts\nimport { test, expect } from '../fixtures/base';\n\ntest('complete purchase flow', async ({ steps }) => {\n  // Navigate and interact\n  await steps.navigateTo('/');\n  await steps.fill('searchInput', 'HomePage', 'Dune');\n  await steps.pressKey('Enter');\n  await steps.waitForNetworkIdle();\n\n  // Verify results\n  const count = await steps.getCount('bookCard', 'HomePage');\n  expect(count).toBeGreaterThan(0);\n\n  // Click first result and add to cart\n  await steps.clickNth('bookCard', 'HomePage', 0);\n  await steps.click('addToCartButton', 'BookDetailPage');\n\n  // Verify cart\n  await steps.navigateTo('/cart');\n  await steps.verifyPresence('total', 'CartPage');\n  await steps.verifyText('total', 'CartPage');\n});\n```\n\n### 4. Run\n\n```bash\nnpx playwright test\n```\n\n---\n\n## 📱 Mobile Setup (Android + iOS)\n\nSame `steps.*` API, different fixture. Mobile drives native apps via Appium + WebDriverIO; the only thing that changes between web and mobile is which fixture function builds the test object.\n\n### Prerequisites\n\n- **Appium 3+**: `npm i -g appium` then `appium driver install uiautomator2` (Android) and/or `appium driver install xcuitest` (iOS).\n- **Android**: Android SDK, a running emulator or device, `ANDROID_HOME` set on PATH.\n- **iOS**: Xcode with at least one simulator you've booted once, plus `xcrun simctl` on PATH.\n- **Appium server running** before tests start:\n  ```bash\n  appium server --base-path /   # default port 4723\n  ```\n- **Your app binary** — `.apk` for Android, `.app` or `.app.zip` for iOS simulator.\n\n### Fixture\n\n```ts\n// tests/fixtures/mobile.ts\nimport { test as base, expect } from '@playwright/test';\nimport { mobileFixture } from '@civitas-cerebrum/singularity-engine';\n\nexport const test = mobileFixture(base, 'tests/data/mobile-page-repository.json', {\n    capabilities: {\n        platformName: 'Android',\n        'appium:automationName': 'UiAutomator2',\n        'appium:deviceName': 'Android Emulator',\n        'appium:app': '/absolute/path/to/app.apk',\n        'appium:autoGrantPermissions': true,\n        'appium:newCommandTimeout': 120,\n    },\n    // Optional:\n    hostname: '127.0.0.1',       // Appium server — defaults to 127.0.0.1\n    port: 4723,                  // Appium port — defaults to 4723\n    timeout: 30_000,             // Element-action timeout (default 30s)\n    onSessionStarted: async (driver) => {\n        // Optional hook — e.g. force-launch the app on Android:\n        // await driver.terminateApp('com.example.app');\n        // await driver.activateApp('com.example.app');\n    },\n});\n\nexport { expect };\n```\n\niOS caps look essentially the same — swap `platformName`, `automationName`, and add `appium:platformVersion` + pointer to the `.app.zip`:\n\n```ts\ncapabilities: {\n    platformName: 'iOS',\n    'appium:automationName': 'XCUITest',\n    'appium:deviceName': 'iPhone 16 Pro',\n    'appium:platformVersion': '18.3',\n    'appium:app': '/absolute/path/to/TestApp.app.zip',\n    'appium:newCommandTimeout': 120,\n}\n```\n\n### page-repository.json for mobile\n\nEntries use the portable `accessibility:` selector (maps to `accessibilityIdentifier` on iOS and `content-desc` on Android). Tag each entry's owning page with the target platform so element-repository picks the right variant:\n\n```json\n{\n  \"pages\": [\n    {\n      \"name\": \"LoginPage\",\n      \"platform\": \"android\",\n      \"elements\": [\n        { \"elementName\": \"submitButton\", \"selector\": { \"accessibility\": \"btn-submit\" } }\n      ]\n    },\n    {\n      \"name\": \"LoginPage\",\n      \"platform\": \"ios\",\n      \"elements\": [\n        { \"elementName\": \"submitButton\", \"selector\": { \"accessibility\": \"btn-submit\" } }\n      ]\n    }\n  ]\n}\n```\n\n### Writing tests (same `steps.*` API)\n\n```ts\nimport { test, expect } from './fixtures/mobile';\n\ntest('login flow', async ({ steps }) => {\n    await steps.fill('emailInput', 'LoginPage', 'user@example.com');\n    await steps.fill('passwordInput', 'LoginPage', 'hunter2');\n    await steps.click('submitButton', 'LoginPage');\n    await steps.on('welcomeBanner', 'HomePage').text.toContain('Welcome');\n});\n```\n\nWeb-only methods (`hover`, `rightClick`, `uploadFile`, `selectDropdown`, `urlContains`, `setViewport`, `reload`, `switchToNewTab`, …) are on the same contract on mobile — they log a `tester:warn` line and either no-op (void returns) or throw `PlatformNotSupportedError` (value returns). Your test code is portable; what doesn't make sense on a given backend fails loudly rather than silently producing fake data.\n\n### Running\n\n```bash\n# Android\nMOBILE_PLATFORM=android npx playwright test\n\n# iOS\nMOBILE_PLATFORM=ios npx playwright test\n```\n\nEngine's own mobile suite (under `tests/mobile/`) reads capabilities from `tests/mobile/capabilities.{android,ios}.json` and lets these env vars override specific keys without editing the JSON:\n\n| Env var | Overrides |\n|---|---|\n| `APPIUM_HOST` / `APPIUM_PORT` | Appium server endpoint |\n| `APPIUM_ANDROID_APP_PATH` / `APPIUM_ANDROID_DEVICE` | Android app + device name |\n| `APPIUM_IOS_APP_PATH` / `APPIUM_IOS_DEVICE` / `APPIUM_IOS_PLATFORM_VERSION` | iOS app + device + iOS version |\n| `MOBILE_ELEMENT_TIMEOUT` | Per-call element timeout |\n| `MOBILE_PAGE_REPOSITORY` | Alternate page-repository.json path |\n\nSee `tests/mobile/README.md` for the engine-specific setup playbook (device bundle ids, iOS Xcode version notes, Appium noReset trade-offs).\n\n---\n\n## ✨ Features\n\n### Semantic Steps API\n\n77 methods organised by purpose. No raw Playwright calls needed — every interaction goes through the Steps API:\n\n```ts\n// All interactions use (elementName, pageName) from the repository\nawait steps.click('submitButton', 'LoginPage');\nawait steps.fill('emailInput', 'LoginPage', 'user@example.com');\nawait steps.verifyText('errorMessage', 'LoginPage', 'Invalid credentials');\n```\n\n### 🌐 API Interaction\n\nMake typed HTTP requests directly from tests. Powered by `@civitas-cerebrum/wasapi`:\n\n```ts\n// Enable in fixture\nexport const test = baseFixture(base, 'tests/data/page-repository.json', {\n  apiBaseUrl: 'http://localhost:8080',\n});\n\n// In tests\nconst response = await steps.apiGet<{ status: string }>('/api/health');\nawait steps.verifyApiStatus(response, 200);\nexpect(response.body!.status).toBe('healthy');\n\nconst order = await steps.apiPost<Order>('/api/orders', {\n  items: [{ bookId: 'book-001', quantity: 1 }]\n});\nexpect(order.body!.status).toBe('COMPLETED');\n\n// Check response headers\nconst headers = await steps.apiHead('/api/health');\nexpect(headers['content-type']).toContain('application/json');\n```\n\n### Multiple API Providers\n\nConnect to multiple backends simultaneously:\n\n```ts\nexport const test = baseFixture(base, 'tests/data/page-repository.json', {\n  apiBaseUrl: 'http://localhost:8080',         // default\n  apiProviders: {\n    payments: 'https://api.stripe.test/v1',    // named\n    auth: 'https://auth.example.com',          // named\n  },\n});\n\n// Default client\nawait steps.apiGet('/api/books');\n\n// Named providers\nawait steps.apiPost('payments', '/charges', { amount: 1000 });\nawait steps.apiGet('auth', '/oauth/userinfo');\n```\n\n### Visibility — Probe + Gate (`isVisible`)\n\n`isVisible()` is a dual-behavior entry point: **await it** for a boolean probe (never throws), or **chain an action** on it for a visibility-gated call (silently skips when hidden).\n\n```ts\n// Probe — returns boolean\nconst ok = await steps.isVisible('promoBanner', 'HomePage', { timeout: 500 });\n\n// Gate — click only if visible (no throw)\nawait steps.isVisible('cookieBanner', 'HomePage').click();\n\n// Gate with text filter\nawait steps.isVisible('promo', 'HomePage', { containsText: '50% off' }).click();\n\n// Matcher tree — silently skipped when hidden\nawait steps.isVisible('banner', 'HomePage').text.toBe('Hello');\n```\n\nEvery probe and gate decision is logged under the `tester:visible` debug channel so silently-skipped actions stay traceable.\n\n`isPresent(elementName, pageName)` remains available as a boolean-only presence check using the default element timeout.\n\n```ts\nconst hasError = await steps.isPresent('errorMessage', 'LoginPage');\nif (hasError) {\n  const errorText = await steps.getText('errorMessage', 'LoginPage');\n  console.log('Login failed:', errorText);\n}\n```\n\n> **`ifVisible()` on the fluent chain is deprecated** — use `isVisible({ timeout })` instead. It covers both the old modifier and the old boolean probe in one API.\n\n### Chain-Style Expect Matchers\n\nA fluent assertion surface for when you want to verify multiple things about a single element. Available at both `steps.expect(el, page)` (top-level) and as field getters on `steps.on(el, page)` (fluent). Each matcher queues an assertion; awaiting flushes the queue and short-circuits on the first failure.\n\n```ts\n// Chain many verifications in one expression\nawait steps.on('submitButton', 'CheckoutPage')\n  .text.toBe('Place Order')\n  .visible.toBeTrue()\n  .enabled.toBeTrue()\n  .attributes.get('data-variant').toBe('primary')\n  .not.attributes.toHaveKey('disabled');\n\n// Top-level matcher tree\nawait steps.expect('price', 'ProductPage').text.toMatch(/^\\$\\d+\\.\\d{2}$/);\nawait steps.expect('items', 'ListPage').count.toBeGreaterThan(3);\n\n// Predicate escape hatch — for assertions the matcher tree doesn't cover\nawait steps.expect('price', 'ProductPage')\n  .satisfy(el => parseFloat(el.text.slice(1)) > 10)\n  .throws('price must be above $10');\n\n// Per-call timeout override\nawait steps.on('slowWidget', 'HomePage').timeout(5000).text.toBe('Ready');\n```\n\nField matchers: `text`, `value`, `count`, `visible`, `enabled`, `attributes`, `css(prop)`. Each carries `.not` for negation.\n\n### Navigation with Query Parameters\n\n```ts\nawait steps.navigateTo('/', { query: { genre: 'Fiction', page: '2' } });\n// Navigates to /?genre=Fiction&page=2\n```\n\n### Email Testing\n\nSend, receive, and verify emails via SMTP/IMAP:\n\n```ts\nexport const test = baseFixture(base, 'tests/data/page-repository.json', {\n  emailCredentials: {\n    smtp: { host: 'smtp.example.com', port: 587, user: '...', pass: '...' },\n    imap: { host: 'imap.example.com', port: 993, user: '...', pass: '...' },\n  },\n});\n\n// In tests\nawait steps.sendEmail({ to: 'user@example.com', subject: 'OTP', text: '123456' });\nconst email = await steps.receiveEmail({\n  filters: [{ type: EmailFilterType.SUBJECT, value: 'OTP' }]\n});\nexpect(email.text).toContain('123456');\n```\n\n### Platform-Agnostic Testing\n\nThe same test code runs on web, Android, iOS, Windows, and Mac:\n\n```ts\n// This test works on all platforms — no code changes needed\nawait steps.navigateTo('/');\nawait steps.click('submitButton', 'HomePage');\nawait steps.verifyText('welcomeMessage', 'HomePage', 'Hello!');\n```\n\nThe abstract factory pattern routes each call to the correct platform implementation. `PlatformElement` wraps both Playwright's `Locator` and WebdriverIO's `Element` — neither leaks into test code.\n\n### Viewport Testing\n\n```ts\nawait steps.setViewport(375, 812);  // mobile\nawait steps.navigateTo('/');\nawait steps.verifyPresence('bookGrid', 'HomePage');\n\nawait steps.setViewport(1280, 720); // desktop\n```\n\n### Screenshots\n\n```ts\nconst pageScreenshot = await steps.screenshot();\nconst elementScreenshot = await steps.screenshot('bookGrid', 'HomePage');\n```\n\n### Listed Element Interactions\n\nWork with tables, lists, and repeating elements:\n\n```ts\n// Click a specific row by text\nawait steps.clickListedElement('orderCard', 'OrdersPage', { text: 'ORD-123' });\n\n// Verify data within a list item\nawait steps.verifyListedElement('orderCard', 'OrdersPage', {\n  text: 'ORD-123',\n  child: { elementName: 'statusBadge', pageName: 'OrdersPage' },\n  expectedText: 'COMPLETED'\n});\n\n// Extract data from a list item\nconst price = await steps.getListedElementData('cartItem', 'CartPage', {\n  text: 'Dune',\n  child: { elementName: 'itemPrice', pageName: 'CartPage' }\n});\n```\n\n---\n\n## 🧩 Included Fixtures\n\n`baseFixture` provides these fixtures to every test:\n\n| Fixture | Type | Description |\n|---------|------|-------------|\n| `steps` | `Steps` | The full Steps API |\n| `repo` | `ElementRepository` | Direct repository access for advanced queries |\n| `interactions` | `ElementInteractions` | Raw interaction API for custom locators |\n| `contextStore` | `ContextStore` | Shared in-memory key-value store between steps |\n| `page` | `Page` | Playwright page with auto-screenshot on failure |\n\n---\n\n## 🛠️ API Reference\n\n### 🧭 Navigation\n\n| Method | Description |\n|--------|-------------|\n| `navigateTo(url, options?)` | Navigate to URL. Options: `{ query: { key: 'value' } }` |\n| `refresh()` | Reload current page |\n| `backOrForward(direction)` | Browser history: `'back'` or `'forward'` |\n| `setViewport(width, height)` | Resize viewport |\n| `switchToNewTab(action)` | Execute action that opens new tab, return new page |\n| `closeTab(targetPage?)` | Close a tab |\n| `getTabCount()` | Number of open tabs |\n\n### 🖱️ Interaction\n\n| Method | Description |\n|--------|-------------|\n| `click(element, page)` | Click an element |\n| `clickNth(element, page, index)` | Click by zero-based index |\n| `clickIfPresent(element, page)` | Click if visible, returns `boolean` |\n| `clickRandom(element, page)` | Click a random match |\n| `doubleClick(element, page)` | Double-click |\n| `rightClick(element, page)` | Right-click (web only) |\n| `hover(element, page)` | Hover (web only) |\n| `fill(element, page, text)` | Clear and fill input |\n| `clearInput(element, page)` | Clear input field |\n| `typeSequentially(element, page, text, delay?)` | Type character by character |\n| `pressKey(key)` | Press keyboard key (`'Enter'`, `'Escape'`, `'Tab'`, etc.) |\n| `check(element, page)` / `uncheck(element, page)` | Toggle checkbox |\n| `selectDropdown(element, page, options?)` | Select from `<select>` |\n| `selectMultiple(element, page, values)` | Multi-select |\n| `uploadFile(element, page, filePath)` | Upload file (web only) |\n| `dragAndDrop(element, page, options)` | Drag to target or by offset |\n| `setSliderValue(element, page, value)` | Set range input value |\n| `scrollIntoView(element, page)` | Scroll element into viewport |\n| `scrollUntilFound(element, page, timeout?)` | Scroll until element appears |\n| `clickWithoutScrolling(element, page)` | Click without auto-scrolling |\n\n### 📊 Data Extraction\n\n| Method | Description |\n|--------|-------------|\n| `getText(element, page)` | Get trimmed text content |\n| `getAttribute(element, page, attr)` | Get HTML attribute value |\n| `getCount(element, page)` | Count matching elements |\n| `getInputValue(element, page)` | Get input's `value` property |\n| `getAll(element, page, options?)` | Get text/attributes from all matches |\n| `getCssProperty(element, page, property)` | Get computed CSS (web only) |\n| `screenshot()` / `screenshot(element, page)` | Page or element screenshot |\n\n### ✅ Verification\n\n| Method | Description |\n|--------|-------------|\n| `verifyPresence(element, page)` | Assert visible (throws on failure) |\n| `isPresent(element, page)` | Boolean visibility check (never throws) |\n| `isVisible(element, page, opts?)` | **VisibleChain** — `await` for probe (boolean); chain `.click()`/`.fill()`/matcher tree for visibility-gated actions. Options: `{ timeout, containsText }` |\n| `expect(element, page)` | Chain-style matcher tree — `.text`, `.value`, `.count`, `.visible`, `.enabled`, `.attributes`, `.css(prop)`, `.satisfy(pred)`, `.not` |\n| `verifyAbsence(element, page)` | Assert hidden/detached |\n| `verifyText(element, page, expected?)` | Assert text matches; no args = not empty |\n| `verifyTextContains(element, page, substring)` | Assert text contains |\n| `verifyCount(element, page, options)` | Assert count: `{ exactly }`, `{ greaterThan }`, `{ lessThan }` |\n| `verifyState(element, page, state)` | Assert state: `'enabled'`, `'disabled'`, `'checked'`, `'visible'`, etc. |\n| `verifyAttribute(element, page, attr, expected)` | Assert attribute value |\n| `verifyInputValue(element, page, expected)` | Assert input value |\n| `verifyUrlContains(text)` | Assert URL contains text |\n| `verifyImages(element, page)` | Verify images rendered (web only) |\n| `verifyOrder(element, page, expectedTexts)` | Assert text order in list |\n| `verifyListOrder(element, page, direction)` | Assert sorted: `'asc'` or `'desc'` |\n| `verifyCssProperty(element, page, prop, expected)` | Assert CSS value (web only) |\n| `verifyTabCount(expected)` | Assert tab count |\n\n### 🌐 API Interaction\n\n| Method | Description |\n|--------|-------------|\n| `apiGet<T>(path, options?)` | GET request. Options: `{ query, headers }` |\n| `apiPost<T>(path, body?, options?)` | POST request. Options: `{ pathParams, query, headers }` |\n| `apiPut<T>(path, body?, options?)` | PUT request |\n| `apiDelete<T>(path, options?)` | DELETE request |\n| `apiPatch<T>(path, body?, options?)` | PATCH request |\n| `apiHead(path)` | HEAD request — returns headers only |\n| `verifyApiStatus(response, status)` | Assert HTTP status code |\n| `verifyApiHeader(response, name, value?)` | Assert header presence/value |\n\nAll API methods support named providers: `apiGet('providerName', path, options?)`.\n\n### 📋 Listed Elements\n\n| Method | Description |\n|--------|-------------|\n| `clickListedElement(element, page, options)` | Click within a list by text/attribute |\n| `verifyListedElement(element, page, options)` | Verify within a list |\n| `getListedElementData(element, page, options)` | Extract data from a list item |\n| `dragAndDropListedElement(element, page, text, options)` | Drag a list item |\n\n### ⏳ Wait & Composite\n\n| Method | Description |\n|--------|-------------|\n| `waitForState(element, page, state?)` | Wait for element state (default: `'visible'`) |\n| `waitForNetworkIdle()` | Wait for network quiet |\n| `waitForResponse(urlPattern, action)` | Wait for network response during action |\n| `waitAndClick(element, page)` | Wait for visible, then click |\n| `fillForm(page, fields)` | Fill multiple form fields in one call |\n| `retryUntil(action, verify, maxRetries?, delay?)` | Retry action until verification passes |\n\n### 📧 Email\n\n| Method | Description |\n|--------|-------------|\n| `sendEmail(options)` | Send via SMTP |\n| `receiveEmail(options)` | Poll inbox for matching email |\n| `receiveAllEmails(options)` | Get all matching emails |\n| `cleanEmails(options?)` | Delete matching or all emails |\n| `markEmail(action, options?)` | Mark as read, flagged, archived |\n\n---\n\n## 🗂️ Page Repository\n\nAll selectors live in a single JSON file — the source of truth for element locations:\n\n```json\n{\n  \"pages\": [\n    {\n      \"name\": \"LoginPage\",\n      \"elements\": [\n        {\n          \"elementName\": \"emailInput\",\n          \"selector\": { \"css\": \"input[data-testid='email']\" }\n        },\n        {\n          \"elementName\": \"submitButton\",\n          \"selector\": {\n            \"css\": \"button[type='submit']\",\n            \"text\": \"Sign In\"\n          }\n        }\n      ]\n    }\n  ]\n}\n```\n\n**Selector strategies:** `css`, `xpath`, `id`, `text`, `accessibility id`, `uiautomator`, `predicate`, `class chain`\n\n**Naming conventions:**\n- Page names: PascalCase (`CheckoutPage`, `ProductDetailsPage`)\n- Element names: camelCase (`submitButton`, `cartTotal`)\n\n### Multi-Platform Selectors\n\nThe same JSON file supports different selectors per platform:\n\n```json\n{\n  \"pages\": [\n    {\n      \"name\": \"HomePage\",\n      \"elements\": [\n        { \"elementName\": \"submit\", \"selector\": { \"css\": \"button#submit\" } }\n      ]\n    },\n    {\n      \"name\": \"HomePage\",\n      \"platform\": \"android\",\n      \"elements\": [\n        { \"elementName\": \"submit\", \"selector\": { \"accessibility id\": \"submit-btn\" } }\n      ]\n    }\n  ]\n}\n```\n\n---\n\n## 📱 Supported Platforms\n\n| Platform | Driver | Status |\n|----------|--------|--------|\n| Web (Chromium, Firefox, WebKit) | Playwright | Stable |\n| Android | Appium + WebdriverIO | Stable |\n| iOS | Appium + WebdriverIO | Stable |\n| Windows | Appium + WinAppDriver | Experimental |\n| Mac | Appium + mac2 driver | Experimental |\n\n---\n\n## 🧱 Advanced Usage\n\n### Direct Repository Access\n\n```ts\ntest('advanced query', async ({ repo, page }) => {\n  const element = await repo.get('submitButton', 'HomePage');\n  const selector = repo.getSelector('submitButton', 'HomePage');\n  const allElements = await repo.getAll('productCard', 'HomePage');\n});\n```\n\n### Context Store\n\nShare data between test steps:\n\n```ts\ntest('multi-step flow', async ({ steps, contextStore }) => {\n  // Save data in one step\n  const orderId = await steps.getText('orderId', 'OrderPage');\n  contextStore.set('orderId', orderId);\n\n  // Use it later\n  const savedId = contextStore.get<string>('orderId');\n  await steps.navigateTo(`/orders/${savedId}`);\n});\n```\n\n### Form Filling\n\nFill multiple fields in one call:\n\n```ts\nawait steps.fillForm('SignupPage', {\n  usernameInput: 'john_doe',\n  emailInput: 'john@example.com',\n  passwordInput: 'SecurePass123!',\n  countrySelect: { type: DropdownSelectType.VALUE, value: 'us' }\n});\n```\n\n### Retry Until\n\nRetry an action until a verification passes:\n\n```ts\nawait steps.retryUntil(\n  async () => { await steps.click('refreshButton', 'DashboardPage'); },\n  async () => { await steps.verifyText('status', 'DashboardPage', 'Ready'); },\n  3,     // max retries\n  1000   // delay between retries (ms)\n);\n```\n\n---\n\n## 📦 Re-exported Types\n\nThese types are available directly from the package:\n\n```ts\nimport {\n  // Framework\n  Steps, baseFixture, BaseFixtureOptions,\n  ElementInteractions, ContextStore,\n\n  // Element Repository\n  ElementType, WebElement, PlatformElement,\n\n  // Enums\n  DropdownSelectType, EmailFilterType, EmailMarkAction,\n\n  // API (wasapi)\n  WasapiClient, ApiCall, ApiResponse, ResponsePair,\n  GET, POST, PUT, DELETE, PATCH,\n  FailedCallException, WasapiException, HttpMethod,\n\n  // Email\n  EmailClient,\n} from '@civitas-cerebrum/singularity-engine';\n```\n\n---\n\n## 📄 License\n\nMIT\n\n---\n\n*Powered by [Civitas Cerebrum](https://civitas-cerebrum.com)*\n","readmeFilename":"README.md"}