UNPKG

@chromatic-com/storybook

Version:

Catch unexpected visual changes & UI bugs in your stories

40 lines (33 loc) • 12.8 kB
'use strict'; var fs = require('fs'); var promises = require('fs/promises'); var path = require('path'); var xe = require('storybook/internal/telemetry'); var node = require('chromatic/node'); var Be = require('storybook/internal/core-events'); var filesize = require('filesize'); var jsonfile = require('jsonfile'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var xe__namespace = /*#__PURE__*/_interopNamespace(xe); var Be__namespace = /*#__PURE__*/_interopNamespace(Be); var he=Object.defineProperty;var Ce=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var be=Object.prototype.hasOwnProperty;var B=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(r,e)=>(typeof require<"u"?require:r)[e]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+t+'" is not supported')});var $=(t,r,e,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let n of _e(r))!be.call(t,n)&&n!==e&&he(t,n,{get:()=>r[n],enumerable:!(o=Ce(r,n))||o.enumerable});return t},v=(t,r,e)=>($(t,r,"default"),e&&$(e,r,"default"));var C={};v(C,xe__namespace);var _={};v(_,Be__namespace);var {CHROMATIC_INDEX_URL:Ie,CHROMATIC_BASE_URL:w=Ie||"https://www.chromatic.com",CHROMATIC_API_URL:Ue=`${w}/api`}=process.env,j="@chromatic-com/storybook",l="chromaui/addon-visual-tests",V=`${l}/test-provider`,z=`${l}/configInfo`,K=`${l}/gitInfo`,H=`${l}/gitInfoError`,J=`${l}/projectInfo`,Y=`${l}/startBuild`,Q=`${l}/stopBuild`,q=`${l}/localBuildProgress`,W=`${l}/telemetry`,X=`${l}/removeAddon`;var Z=`${l}/ChannelFetch/aborted`,ee=`${l}ChannelFetch/request`,U=`${l}ChannelFetch/response`,{TESTING_MODULE_CRASH_REPORT:Qe="testingModuleCrashReport",TESTING_MODULE_PROGRESS_REPORT:te="testingModuleProgressReport",TESTING_MODULE_RUN_REQUEST:qe="testingModuleRunRequest",TESTING_MODULE_RUN_ALL_REQUEST:We="testingModuleRunAllRequest",TESTING_MODULE_CANCEL_TEST_RUN_REQUEST:Xe="testingModuleCancelTestRunRequest",TESTING_MODULE_CANCEL_TEST_RUN_RESPONSE:Ze="testingModuleCancelTestRunResponse",TESTING_MODULE_WATCH_MODE_REQUEST:et="testingModuleWatchModeRequest"}=_,re={autoAcceptChanges:!1,exitOnceUploaded:!1,exitZeroOnChanges:!0,forceRebuild:!0,fromCI:!1,interactive:!1,isLocalBuild:!0,skip:!1,skipUpdateCheck:!0,storybookBuildDir:void 0};var I=t=>T.includes(t),ne=t=>["upload","snapshot"].includes(t),T=["initialize","build","upload","verify","snapshot"],A={initialize:{key:"initialize",emoji:"\u{1F680}",renderName:()=>"Initialize build",renderProgress:()=>"Initializing build...",renderComplete:()=>"Initialized",estimateDuration:2e3},build:{key:"build",emoji:"\u{1F3D7}",renderName:()=>"Build Storybook",renderProgress:()=>"Building your Storybook...",renderComplete:()=>"Storybook built",estimateDuration:2e4},upload:{key:"upload",emoji:"\u{1F4E1}",renderName:()=>"Publish your Storybook",renderProgress:({stepProgress:t})=>{let{numerator:r,denominator:e}=t.upload;if(!e||!r)return "Uploading files...";let{value:o,exponent:n}=filesize.filesize(e,{output:"object",round:1}),{value:s,symbol:i}=filesize.filesize(r,{exponent:n,output:"object",round:1});return `Uploading files... ${s}/${o} ${i}`},renderComplete:()=>"Publish complete",estimateDuration:2e4},verify:{key:"verify",emoji:"\u{1F50D}",renderName:()=>"Verify your Storybook",renderProgress:()=>"Verifying contents...",renderComplete:()=>"Storybook verified",estimateDuration:2e4},snapshot:{key:"snapshot",emoji:"\u{1F4F8}",renderName:()=>"Run visual tests",renderProgress:({stepProgress:t})=>{let{numerator:r,denominator:e}=t.snapshot;return e?`Running visual tests... ${r}/${e}`:"Running visual tests..."},renderComplete:()=>"Tested your stories",estimateDuration:9e4},aborted:{key:"aborted",emoji:"\u270B",renderName:()=>"Build canceled",renderProgress:()=>"Build canceled",renderComplete:()=>"Build canceled",estimateDuration:0},complete:{key:"complete",emoji:"\u{1F389}",renderName:()=>"Visual tests completed!",renderProgress:()=>"Visual tests completed!",renderComplete:()=>"Visual tests completed!",estimateDuration:0},error:{key:"error",emoji:"\u{1F6A8}",renderName:()=>"Build failed",renderProgress:()=>"Build failed",renderComplete:()=>"Build failed",estimateDuration:0},limited:{key:"error",emoji:"\u{1F6A8}",renderName:()=>"Build limited",renderProgress:()=>"Build limited",renderComplete:()=>"Build limited",estimateDuration:0}},Se={buildProgressPercentage:0,currentStep:T[0],stepProgress:Object.fromEntries(T.map(t=>[t,{}]))},se=JSON.stringify(Se);var ie=2e3,S,Pe=(t,r)=>{if(!I(t))throw new Error(`Unknown step: ${t}`);let e=T.map(d=>{let{startedAt:p,completedAt:g}=r?.[d]||{};return p&&g?g-p:A[d].estimateDuration}),o=e.reduce((d,p)=>d+p,0),n=T.indexOf(t),s=e.slice(0,n).reduce((d,p)=>d+p,0),i=s+e[n],a=s/o*100,u=i/o*100;return {...A[t],startPercentage:a,endPercentage:u,stepPercentage:u-a}},F=(t,r)=>(e,{progress:o,total:n}={})=>{if(clearTimeout(r),!I(e.task))return;if(!t.value)throw new Error("Unexpected missing value for localBuildProgress");let{buildProgressPercentage:s,stepProgress:i,previousBuildProgress:a}=t.value;if(i[e.task]?.completedAt)return;let{startPercentage:u,endPercentage:d,stepPercentage:p}=Pe(e.task,a),g=u;if(o&&n&&(g+=p*(o/n)),!ne(e.task)){let{estimateDuration:x}=A[e.task],y=T.indexOf(e.task);g=Math.max(g,s)+ie/x*p,r=setTimeout(()=>{if(!t.value)throw new Error("Unexpected missing value for localBuildProgress");let{currentStep:P}=t.value;I(P)&&T.indexOf(P)<=y&&F(t,r)(e);},ie);}i[e.task]={startedAt:Date.now(),...i[e.task],...o&&n&&{numerator:o,denominator:n}},t.value={buildId:e.announcedBuild?.id,branch:e.git?.branch,buildProgressPercentage:Math.min(g,d),currentStep:e.task,stepProgress:i};},ae=(t,r)=>(e,o)=>{if(clearTimeout(r),!t.value)throw new Error("Unexpected missing value for localBuildProgress");let{buildProgressPercentage:n,stepProgress:s}=t.value,i={buildId:e.announcedBuild?.id,branch:e.git?.branch,buildProgressPercentage:n,stepProgress:s,previousBuildProgress:s};if(o){t.value={...i,currentStep:S?.signal.aborted?"aborted":"error",formattedError:o.formattedError,originalError:o.originalError};return}e.task&&I(e.task)&&(s[e.task]={...s[e.task],completedAt:Date.now()}),e.task==="verify"&&e.build?.wasLimited&&(t.value={...i,currentStep:"limited",stepProgress:s,errorDetailsUrl:e.build?.app.account?.billingUrl}),e.build&&e.task==="snapshot"&&(t.value={...i,buildProgressPercentage:100,currentStep:"complete",stepProgress:s,changeCount:e.build.changeCount,errorCount:e.build.errorCount});},le=async(t,r)=>{if(!r.projectId)throw new Error("Missing projectId");if(!r.userToken)throw new Error("Missing userToken");t.value=JSON.parse(se);let e;S?.abort(),S=new AbortController,process.env.SB_TESTBUILD="true",await node.run({flags:{interactive:!1},options:{...r,...re,logPrefix:"\x1B[38;5;202mChromatic\x1B[0m:",experimental_onTaskStart:F(t,e),experimental_onTaskProgress:F(t,e),experimental_onTaskComplete:ae(t,e),experimental_onTaskError:ae(t,e),experimental_abortSignal:S?.signal}});},ce=()=>{S?.abort(new Error("Build canceled from Storybook"));};var M=new Map,b=class{constructor(r,e=fetch){this.channel=r,this.abortControllers=new Map,this.channel.on(Z,({requestId:o})=>{this.abortControllers.get(o)?.abort(),this.abortControllers.delete(o);}),this.channel.on(ee,async({requestId:o,input:n,init:s})=>{let i=new AbortController;this.abortControllers.set(o,i);try{let a=await e(n,{...s,signal:i.signal}),u=await a.text(),d=Array.from(a.headers),p={body:u,headers:d,status:a.status,statusText:a.statusText};this.channel.emit(U,{requestId:o,response:p});}catch(a){let u=a instanceof Error?a.message:String(a);this.channel.emit(U,{requestId:o,error:u});}finally{this.abortControllers.delete(o);}});}static subscribe(r,e,o=fetch){let n=M.get(r)||new b(e,o);return M.has(r)||M.set(r,n),n}};var ue="experimental_useSharedState_getValue",L="experimental_useSharedState_setValue",G=new Map,f=class{constructor(r){this.channel=r,this.listeners=[],this.state={},this.channel.on(L,(e,o,n)=>{this.state?.[e]?.index>=n||(this.state[e]={index:n,value:o});}),this.channel.on(ue,e=>{let o=this.state[e]?.index??0,n=this.state[e]?.value;this.channel.emit(L,e,n,o);});}get(r){return this.state[r]||this.channel.emit(ue,r),this.state[r]?.value}set(r,e){let o=(this.state[r]?.index??0)+1;this.state[r]={index:o,value:e},this.channel.emit(L,r,e,o);}static subscribe(r,e){let o=G.get(r)||new f(e);return G.has(r)||(G.set(r,o),o.channel.on(L,(n,s)=>{n===r&&o.listeners.forEach(i=>i(s));})),{get value(){return o.get(r)},set value(n){o.set(r,n);},on(n,s){if(n!=="change")throw new Error("unsupported event");o.listeners.push(s);},off(n,s){if(n!=="change")throw new Error("unsupported event");let i=o.listeners.indexOf(s);i>=0&&o.listeners.splice(i,1);}}}};async function de(t,r){let e=Object.entries(r).sort((o,n)=>o[0].localeCompare(n[0])).reduce((o,[n,s])=>s===null?o:Object.assign(o,{[n]:s}),{});await jsonfile.writeFile(t,e,{spaces:2});}function ve(t=[]){return [...t,B.resolve("./manager.mjs")]}var Ee=async()=>{let t=(async()=>{try{let r=B.resolve("@chromatic-com/storybook/package.json"),e=await promises.readFile(r,"utf-8");return JSON.parse(e).version||null}catch{return null}})();return Ee=()=>t,t},me=(t,r,e)=>Object.fromEntries(Object.entries(e).map(([o,n])=>[o,n===r[o]?null:n]).filter(([o,n])=>n!==null||t[o]!==void 0)),fe=async(t,r)=>{let e={storybookBaseDir:".",storybookConfigDir:".storybook"},o={},n={},{repositoryRootDir:s}=await node.getGitInfo(),i=s&&path.normalize(path.relative(s,process.cwd()));i!==path.normalize(t.storybookBaseDir??"")&&(o.storybookBaseDir=i);let a=path.normalize(path.relative(process.cwd(),r.configDir));return a!==path.normalize(t.storybookConfigDir??"")&&(o.storybookConfigDir=a),t.onlyChanged===void 0&&(n.onlyChanged=!0),t.zip===void 0&&(n.zip=!0),{configuration:t,problems:me(t,e,o),suggestions:me(t,e,n)}},we=(t,r,e,o)=>{let n,s,i,a=async()=>{try{let u=await node.getGitInfo();Object.entries(u).some(([d,p])=>n?.[d]!==p)&&r(u,n),n=u,s=void 0,i=setTimeout(a,t);}catch(u){e(u),o&&s?.message!==u.message&&(console.error(`Failed to fetch git info, with error: ${u}`),n=void 0,s=u),i=setTimeout(a,t);}};return a(),{cancel:()=>clearTimeout(i)}},Ae=async(t,r)=>{let e=await node.getConfiguration(t);await r(e),e.configFile&&fs.watch(e.configFile,async(o,n)=>{n&&await r(await node.getConfiguration(n));});};async function Le(t,r){let{configFile:e,presets:o}=r;b.subscribe(l,t);let n=o.apply("experimental_serverAPI"),s=o.apply("core"),{projectId:i}=await node.getConfiguration(e),a=f.subscribe(J,t);a.value=i?{projectId:i}:{};let u=i;a.on("change",async({projectId:c}={})=>{if(!c||c===u)return;u=c;let E=e;try{let{configFile:m,...O}=await node.getConfiguration(E),R=m||E||"chromatic.config.json",{problems:h,suggestions:D}=await fe(O,r);await de(R,{...O,...h,...D,projectId:c}),a.value={...a.value,written:!0,dismissed:!1,configFile:R};}catch(m){console.warn(`Failed to update your main configuration: ${m}`),a.value={...a.value,written:!1,dismissed:!1,configFile:E};}});let d=f.subscribe(q,t),p={initialize:"pending",build:"pending",upload:"pending",verify:"pending",snapshot:"pending",complete:"success",error:"failed",limited:"failed",aborted:"success"};d.on("change",c=>{if(!c)return;let{currentStep:E,stepProgress:m,errorCount:O=0,changeCount:R=0}=c,h=m.snapshot?.denominator,D=O+R,Te=m.snapshot?.completedAt?new Date(m.snapshot.completedAt):new Date;t.emit(te,{providerId:V,status:p[E],cancellable:["initialize","build","upload"].includes(E),progress:{numFailedTests:D,numPassedTests:h&&h-D,numPendingTests:h&&h-(m.snapshot.numerator||0),numTotalTests:h,startedAt:m.initialize?.startedAt&&new Date(m.initialize.startedAt),finishedAt:["aborted","complete","error","limited"].includes(E)?Te:void 0},details:c});}),t.on(Y,async({accessToken:c})=>{let{projectId:E}=a.value||{};try{await le(d,{configFile:e,projectId:E,userToken:c});}catch(m){console.error(`Failed to run Chromatic build, with error: ${m}`);}}),t.on(Q,ce),t.on(W,async c=>{(await s).disableTelemetry||(0, C.telemetry)("addon-visual-tests",{...c,addonVersion:await Ee()});});let g=f.subscribe(z,t),x=f.subscribe(K,t),y=f.subscribe(H,t),P=we(5e3,c=>{y.value=void 0,x.value=c;},c=>{y.value=c;});return Ae(e,async c=>{u&&(g.value=await fe(c,r));}),t.on(X,()=>{n.then(c=>c.removeAddon(j)).catch(c=>console.error(c)),P.cancel();}),t}var Ne={managerEntries:ve,experimental_serverChannel:Le,env:async(t,{configType:r})=>r==="PRODUCTION"?t:{...t,CHROMATIC_BASE_URL:w}},Rt=Ne; module.exports = Rt;