@@ -11,6 +11,7 @@ import type {ObservableApiClientOptions} from "../src/observableApiClient.js";
11
11
import type { GetCurrentUserResponse } from "../src/observableApiClient.js" ;
12
12
import { ObservableApiClient } from "../src/observableApiClient.js" ;
13
13
import type { DeployConfig } from "../src/observableApiConfig.js" ;
14
+ import { stripColor } from "../src/tty.js" ;
14
15
import { MockAuthEffects } from "./mocks/authEffects.js" ;
15
16
import { TestClackEffects } from "./mocks/clack.js" ;
16
17
import { MockConfigEffects } from "./mocks/configEffects.js" ;
@@ -178,11 +179,11 @@ describe("deploy", () => {
178
179
. handleGetCurrentUser ( )
179
180
. handleGetProject ( DEPLOY_CONFIG )
180
181
. handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
181
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
182
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
183
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
184
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
185
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
182
+ . expectFileUpload ( { deployId, path : "index.html" } )
183
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
184
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
185
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
186
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
186
187
. handlePostDeployUploaded ( { deployId} )
187
188
. handleGetDeploy ( { deployId, deployStatus : "uploaded" } )
188
189
. start ( ) ;
@@ -206,11 +207,11 @@ describe("deploy", () => {
206
207
. handleGetProject ( { ...DEPLOY_CONFIG , title : oldTitle } )
207
208
. handleUpdateProject ( { projectId : DEPLOY_CONFIG . projectId , title : TEST_CONFIG . title ! } )
208
209
. handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
209
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
210
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
211
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
212
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
213
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
210
+ . expectFileUpload ( { deployId, path : "index.html" } )
211
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
212
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
213
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
214
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
214
215
. handlePostDeployUploaded ( { deployId} )
215
216
. handleGetDeploy ( { deployId} )
216
217
. start ( ) ;
@@ -234,11 +235,11 @@ describe("deploy", () => {
234
235
. handleGetCurrentUser ( )
235
236
. handleGetProject ( deployConfig )
236
237
. handlePostDeploy ( { projectId : deployConfig . projectId , deployId} )
237
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
238
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
239
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
240
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
241
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
238
+ . expectFileUpload ( { deployId, path : "index.html" } )
239
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
240
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
241
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
242
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
242
243
. handlePostDeployUploaded ( { deployId} )
243
244
. handleGetDeploy ( { deployId} )
244
245
. start ( ) ;
@@ -261,11 +262,11 @@ describe("deploy", () => {
261
262
} )
262
263
. handlePostProject ( { projectId : DEPLOY_CONFIG . projectId } )
263
264
. handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
264
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
265
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
266
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
267
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
268
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
265
+ . expectFileUpload ( { deployId, path : "index.html" } )
266
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
267
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
268
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
269
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
269
270
. handlePostDeployUploaded ( { deployId} )
270
271
. handleGetDeploy ( { deployId} )
271
272
. start ( ) ;
@@ -472,6 +473,7 @@ describe("deploy", () => {
472
473
. handleGetCurrentUser ( )
473
474
. handleGetProject ( DEPLOY_CONFIG )
474
475
. handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
476
+ . handlePostDeployManifest ( { deployId, files : [ { deployId, path : "index.html" , action : "upload" } ] } )
475
477
. handlePostDeployFile ( { deployId, status : 500 } )
476
478
. start ( ) ;
477
479
const effects = new MockDeployEffects ( { deployConfig : DEPLOY_CONFIG } ) ;
@@ -495,11 +497,11 @@ describe("deploy", () => {
495
497
. handleGetCurrentUser ( )
496
498
. handleGetProject ( DEPLOY_CONFIG )
497
499
. handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
498
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
499
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
500
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
501
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
502
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
500
+ . expectFileUpload ( { deployId, path : "index.html" } )
501
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
502
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
503
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
504
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
503
505
. handlePostDeployUploaded ( { deployId, status : 500 } )
504
506
. start ( ) ;
505
507
const effects = new MockDeployEffects ( { deployConfig : DEPLOY_CONFIG } ) ;
@@ -612,11 +614,11 @@ describe("deploy", () => {
612
614
projectId : newProjectId
613
615
} )
614
616
. handlePostDeploy ( { projectId : newProjectId , deployId} )
615
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
616
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
617
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
618
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
619
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
617
+ . expectFileUpload ( { deployId, path : "index.html" } )
618
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
619
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
620
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
621
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
620
622
. handlePostDeployUploaded ( { deployId} )
621
623
. handleGetDeploy ( { deployId} )
622
624
. start ( ) ;
@@ -700,11 +702,11 @@ describe("deploy", () => {
700
702
. handleGetCurrentUser ( )
701
703
. handleGetProject ( DEPLOY_CONFIG )
702
704
. handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
703
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
704
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
705
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
706
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
707
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
705
+ . expectFileUpload ( { deployId, path : "index.html" } )
706
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
707
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
708
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
709
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
708
710
. handlePostDeployUploaded ( { deployId} )
709
711
. handleGetDeploy ( { deployId} )
710
712
. start ( ) ;
@@ -727,11 +729,11 @@ describe("deploy", () => {
727
729
. handleGetCurrentUser ( )
728
730
. handleGetProject ( DEPLOY_CONFIG )
729
731
. handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
730
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
731
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
732
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
733
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
734
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
732
+ . expectFileUpload ( { deployId, path : "index.html" } )
733
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
734
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
735
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
736
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
735
737
. handlePostDeployUploaded ( { deployId} )
736
738
. handleGetDeploy ( { deployId, deployStatus : "created" } )
737
739
. handleGetDeploy ( { deployId, deployStatus : "pending" } )
@@ -831,11 +833,11 @@ describe("deploy", () => {
831
833
. handleGetCurrentUser ( )
832
834
. handleGetProject ( DEPLOY_CONFIG )
833
835
. handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
834
- . handlePostDeployFile ( { deployId, clientName : "index.html" } )
835
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/theme-air,near-midnight.css" } )
836
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/client.js" } )
837
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/runtime.js" } )
838
- . handlePostDeployFile ( { deployId, clientName : "_observablehq/stdlib.js" } )
836
+ . expectFileUpload ( { deployId, path : "index.html" } )
837
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" } )
838
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" } )
839
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" } )
840
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" } )
839
841
. handlePostDeployUploaded ( { deployId} )
840
842
. handleGetDeploy ( { deployId} )
841
843
. start ( ) ;
@@ -860,6 +862,47 @@ describe("deploy", () => {
860
862
861
863
effects . close ( ) ;
862
864
} ) ;
865
+
866
+ it ( "will skip file uploads if instructed by the server" , async ( ) => {
867
+ const deployId = "deploy456" ;
868
+ getCurrentObservableApi ( )
869
+ . handleGetCurrentUser ( )
870
+ . handleGetProject ( DEPLOY_CONFIG )
871
+ . handlePostDeploy ( { projectId : DEPLOY_CONFIG . projectId , deployId} )
872
+ . expectFileUpload ( { deployId, path : "index.html" , action : "upload" } )
873
+ . expectFileUpload ( { deployId, path : "_observablehq/theme-air,near-midnight.css" , action : "skip" } )
874
+ . expectFileUpload ( { deployId, path : "_observablehq/client.js" , action : "skip" } )
875
+ . expectFileUpload ( { deployId, path : "_observablehq/runtime.js" , action : "skip" } )
876
+ . expectFileUpload ( { deployId, path : "_observablehq/stdlib.js" , action : "skip" } )
877
+ . handlePostDeployUploaded ( { deployId} )
878
+ . handleGetDeploy ( { deployId, deployStatus : "uploaded" } )
879
+ . start ( ) ;
880
+
881
+ const effects = new MockDeployEffects ( {
882
+ deployConfig : DEPLOY_CONFIG ,
883
+ fixedInputStatTime : new Date ( "2024-03-09" ) ,
884
+ fixedOutputStatTime : new Date ( "2024-03-10" )
885
+ } ) ;
886
+ effects . clack . inputs = [ "fix some bugs" ] ; // "what changed?"
887
+ await deploy ( TEST_OPTIONS , effects ) ;
888
+
889
+ effects . close ( ) ;
890
+
891
+ // first is the upload spinner, second is the server processing spinner
892
+ assert . equal ( effects . clack . spinners . length , 2 , JSON . stringify ( effects . clack . spinners , null , 2 ) ) ;
893
+ const events = effects . clack . spinners [ 0 ] . _events . map ( ( e ) => {
894
+ const r : { method : string ; message ?: string } = { method : e . method } ;
895
+ if ( e . message ) r . message = stripColor ( e . message ) ;
896
+ return r ;
897
+ } ) ;
898
+ assert . deepEqual ( events , [
899
+ { method : "start" } ,
900
+ { method : "message" , message : "Hashing local files" } ,
901
+ { method : "message" , message : "Sending file manifest to server" } ,
902
+ { method : "message" , message : "1 / 1 uploading index.html" } ,
903
+ { method : "stop" , message : "1 uploaded, 4 unchanged, 5 total." }
904
+ ] ) ;
905
+ } ) ;
863
906
} ) ;
864
907
865
908
describe ( "promptDeployTarget" , ( ) => {
0 commit comments