@shopify/cli
Version:
A CLI tool to build for the Shopify platform
2 lines (1 loc) • 9.27 kB
JavaScript
import{b as E}from"./chunk-XR6GMMEU.js";import{b}from"./chunk-XOTA6JTZ.js";import{M as F,N}from"./chunk-6G6TMKXF.js";import{r as U}from"./chunk-LDGAHMS7.js";import{Nb as k,T as C,Ta as w,ma as O,tb as n}from"./chunk-N5PQPIBF.js";import{k as R}from"./chunk-YTNDFQJT.js";import{d as u,f as P,g as x,h as g,n as T,q as y}from"./chunk-IU2ZQ6TE.js";import{e as H,g as d}from"./chunk-VPRTJUIN.js";d();d();d();d();var m=class{constructor(){this.assertions=[],this.registeredTests=[]}async runSuite(t){this.context=t,this.registeredTests=[];let e=[];this.tests();for(let i of this.registeredTests){this.assertions=[];let s=Date.now();try{await i.fn(),e.push({name:i.name,status:this.hasFailures()?"failed":"passed",duration:Date.now()-s,assertions:[...this.assertions]})}catch(r){e.push({name:i.name,status:"failed",duration:Date.now()-s,assertions:[...this.assertions],error:r instanceof Error?r:new Error(String(r))})}}return e}test(t,e){this.registeredTests.push({name:t,fn:e})}tests(){}async run(t,e){let i=e?.cwd??this.context.workingDirectory,s=await F(t,{cwd:i,env:e?.env});return{command:t,exitCode:s.exitCode,stdout:s.stdout,stderr:s.stderr,output:String(s.stdout)+String(s.stderr),success:s.exitCode===0}}async runInteractive(t,e){let i=e?.cwd??this.context.workingDirectory,s=0;try{await N(t,{cwd:i,env:e?.env,stdin:"inherit"})}catch{s=1}return{command:t,exitCode:s,stdout:"",stderr:"",output:"",success:s===0}}assertSuccess(t,e){this.assertions.push({description:e??`Command succeeded: ${t.command}`,passed:t.success,expected:"exit code 0",actual:`exit code ${t.exitCode}`})}assertError(t,e,i){let s=!t.success;if(e){let h=(typeof e=="string"?new RegExp(e):e).test(t.output),c;s?h?c="matched":c=`output: ${t.output.slice(0,200)}`:c="command succeeded",this.assertions.push({description:i??`Command failed with expected error: ${e}`,passed:s&&h,expected:`failure with error matching ${e}`,actual:c})}else this.assertions.push({description:i??`Command failed: ${t.command}`,passed:s,expected:"non-zero exit code",actual:`exit code ${t.exitCode}`})}async assertFile(t,e,i){let s=g(t)?t:u(this.context.workingDirectory,t),r=x(this.context.workingDirectory,s);if(!await w(s)){this.assertions.push({description:i??`File exists: ${r}`,passed:!1,expected:"file exists",actual:"file not found"});return}if(e){let c=await O(s),D=(typeof e=="string"?new RegExp(e):e).test(c);this.assertions.push({description:i??`File ${r} matches ${e}`,passed:D,expected:`content matching ${e}`,actual:D?"matched":`content: ${c.slice(0,200)}...`})}else this.assertions.push({description:i??`File exists: ${r}`,passed:!0,expected:"file exists",actual:"file exists"})}async assertNoFile(t,e){let i=g(t)?t:u(this.context.workingDirectory,t),s=x(this.context.workingDirectory,i),r=await w(i);this.assertions.push({description:e??`File does not exist: ${s}`,passed:!r,expected:"file does not exist",actual:r?"file exists":"file does not exist"})}async assertDirectory(t,e){let i=g(t)?t:u(this.context.workingDirectory,t),s=x(this.context.workingDirectory,i),r=await w(i);this.assertions.push({description:e??`Directory exists: ${s}`,passed:r,expected:"directory exists",actual:r?"directory exists":"directory not found"})}assertOutput(t,e,i){let r=(typeof e=="string"?new RegExp(e):e).test(t.output);this.assertions.push({description:i??`Output matches ${e}`,passed:r,expected:`output matching ${e}`,actual:r?"matched":`output: ${t.output.slice(0,200)}`})}assertJson(t,e,i){try{let s=JSON.parse(t.stdout);if(e){let r=e(s);this.assertions.push({description:i??"Output is valid JSON matching validator",passed:r,expected:"valid JSON matching validator",actual:r?"matched":"validator returned false"})}else this.assertions.push({description:i??"Output is valid JSON",passed:!0,expected:"valid JSON",actual:"valid JSON"});return s}catch{this.assertions.push({description:i??"Output is valid JSON",passed:!1,expected:"valid JSON",actual:`invalid JSON: ${t.stdout.slice(0,100)}`});return}}assert(t,e){this.assertions.push({description:e,passed:t,expected:"true",actual:String(t)})}assertEqual(t,e,i){this.assertions.push({description:i,passed:t===e,expected:String(e),actual:String(t)})}hasFailures(){return this.assertions.some(t=>!t.passed)}};m.description="Doctor test suite";var p=class extends m{constructor(){super(...arguments);this.themeName="";this.themePath=""}static{this.description="Tests the theme init command creates a valid theme structure"}tests(){this.test("init creates theme directory",async()=>{this.themeName=`doctor-theme-${C("creative")}`,this.themePath=u(this.context.workingDirectory,this.themeName);let e=await this.runInteractive(`shopify theme init ${this.themeName} --path ${this.context.workingDirectory}`);this.assertSuccess(e),this.context.themeName=this.themeName,this.context.themePath=this.themePath}),this.test("essential theme files exist",async()=>{let e=["layout/theme.liquid","config/settings_schema.json","templates/index.json"];await Promise.all(e.map(i=>this.assertFile(u(this.themePath,i))))}),this.test("theme directories exist",async()=>{let e=["sections","snippets","assets","locales"];await Promise.all(e.map(i=>this.assertDirectory(u(this.themePath,i))))}),this.test("layout/theme.liquid has valid content",async()=>{await this.assertFile(u(this.themePath,"layout/theme.liquid"),/<!doctype html>|<html|{{ content_for_header }}/i,"layout/theme.liquid contains expected Liquid markup")})}};d();var l=class extends m{static{this.description="Tests pushing a theme to the store creates an unpublished theme"}static{this.requiresStore=!0}tests(){this.test("push creates unpublished theme",async()=>{if(!this.context.themePath){this.assert(!1,"Theme init did not complete successfully; themePath is missing in context");return}let t=this.context.themeName??"doctor-theme",e=`shopify theme push --unpublished --json --path ${this.context.themePath} -t ${t}`;this.context.environment&&(e+=` -e ${this.context.environment}`),this.context.store&&(e+=` -s ${this.context.store}`),this.context.password&&(e+=` --password ${this.context.password}`);let i=await this.run(e);this.assertSuccess(i);let s=this.assertJson(i,r=>typeof r.theme?.id=="number");s?.theme&&(this.assert(typeof s.theme.id=="number","Theme was created with a valid ID"),this.assertEqual(s.theme.role,"unpublished","Theme role is unpublished"),this.assert(s.theme.editor_url.includes("/admin/themes/"),"Editor URL is provided"),this.assert(s.theme.preview_url.includes("preview_theme_id="),"Preview URL is provided"),this.context.themeId=String(s.theme.id),this.context.data.editorUrl=s.theme.editor_url,this.context.data.previewUrl=s.theme.preview_url)})}};d();function _(o){return{workingDirectory:o.path??y(),environment:o.environment,store:o.store,password:o.password,data:{}}}d();var a=o=>k(o),v;function J(o){v=o}function G(o){if(!v)return o;let t=/\/[^\s,)]+/g;return o.replace(t,e=>T(e,v))}function A(o,t){a(""),a(n.bold(n.cyan(`Suite: ${o}`))),a(n.dim(t))}function j(o){a(n.bold(n.blue(`Running: ${o}`)))}function q(o){let t=`(${(o.duration/1e3).toFixed(2)}s)`;if(o.status==="passed"){a(n.bold(n.green(`PASSED: ${o.name} ${n.dim(t)}`)));for(let e of I(o.assertions))a(e)}else if(o.status==="failed"){a(n.red(`FAILED: ${o.name} ${n.dim(t)}`));for(let e of I(o.assertions))a(e);o.error&&a(n.red(` Error: ${G(o.error.message)}`))}else a(n.yellow(`SKIPPED: ${o.name}`))}function S(o){let t=o.filter(h=>h.status==="passed").length,e=o.filter(h=>h.status==="failed").length,i=o.filter(h=>h.status==="skipped").length,s=o.length,r=o.reduce((h,c)=>h+c.duration,0);a(""),a(n.bold("\u2500".repeat(40))),e>0?a(n.red(n.bold(`Doctor Complete: ${e}/${s} tests failed`))):a(n.green(n.bold(`Doctor Complete: ${t}/${s} tests passed`))),a(` Passed: ${n.green(String(t))}`),a(` Failed: ${n.red(String(e))}`),i>0&&a(` Skipped: ${n.yellow(String(i))}`),a(` Total time: ${n.dim(`${(r/1e3).toFixed(2)}s`)}`)}function I(o){return o.map(t=>{if(t.passed)return n.green(` [OK] ${t.description}`);{let e=` (expected: ${t.expected}, actual: ${t.actual})`;return n.red(` [FAIL] ${t.description}${e}`)}})}var Y=[p,l];async function L(o){let t=[],e=_(o);J(e.workingDirectory);for(let i of Y){let s=new i,r=i.description??"Test suite";A(i.name,r);let h=await s.runSuite(e);for(let c of h)if(j(c.name),q(c),t.push(c),c.status==="failed")return S(t),t}return S(t),t}var f=H(U(),1);var $=class o extends b{static{this.description="Run all theme command doctor-release tests"}static{this.hidden=!0}static{this.flags={...E,path:f.Flags.string({char:"p",description:"The path to run tests in. Defaults to current directory.",env:"SHOPIFY_FLAG_PATH",parse:async t=>P(t),default:async()=>y()}),environment:f.Flags.string({char:"e",description:"The environment to use from shopify.theme.toml (required for store-connected tests).",env:"SHOPIFY_FLAG_ENVIRONMENT",required:!0}),store:f.Flags.string({char:"s",description:"Store URL (overrides environment).",env:"SHOPIFY_FLAG_STORE"}),password:f.Flags.string({description:"Password from Theme Access app (overrides environment).",env:"SHOPIFY_FLAG_PASSWORD"})}}async run(){if(!R())return;let{flags:t}=await this.parse(o);(await L({path:t.path,environment:t.environment,store:t.store,password:t.password})).some(s=>s.status==="failed")&&(process.exitCode=1)}};export{$ as a};