@@ -63,6 +63,8 @@ const listObjectsMock = mock();
6363const getObjectMock = mock ( ) ;
6464const putObjectMock = mock ( ) ;
6565const copyObjectMock = mock ( ) ;
66+ const deleteObjectsMock = mock ( ) ;
67+ const getKeysFromS3Mock = mock ( ) ;
6668const updateBaseImagesMock = mock ( ) ;
6769async function listAllObjects (
6870 input : { Bucket : string ; Prefix : string } ,
@@ -83,11 +85,12 @@ mock.module('shared/s3', () => ({
8385 s3Client : { } ,
8486 listObjects : listObjectsMock ,
8587 listAllObjects,
86- getKeysFromS3 : mock ( ) ,
88+ getKeysFromS3 : getKeysFromS3Mock ,
8789 updateBaseImages : updateBaseImagesMock ,
8890 getObject : getObjectMock ,
8991 putObject : putObjectMock ,
90- copyObject : copyObjectMock
92+ copyObject : copyObjectMock ,
93+ deleteObjects : deleteObjectsMock
9194} ) ) ;
9295
9396const jimpImageMock = {
@@ -215,6 +218,8 @@ describe('main', () => {
215218 getObjectMock . mockResolvedValue ( { Body : null } ) ;
216219 putObjectMock . mockResolvedValue ( { } ) ;
217220 copyObjectMock . mockResolvedValue ( { } ) ;
221+ deleteObjectsMock . mockResolvedValue ( { } ) ;
222+ getKeysFromS3Mock . mockResolvedValue ( [ ] ) ;
218223 updateBaseImagesMock . mockResolvedValue ( undefined ) ;
219224 mkdirMock . mockResolvedValue ( undefined ) ;
220225 readFileMock . mockResolvedValue ( Buffer . from ( 'image-data' ) ) ;
@@ -750,6 +755,59 @@ describe('main', () => {
750755 expect ( createCommitStatusMock ) . toHaveBeenCalled ( ) ;
751756 } ) ;
752757
758+ it ( 'should delete S3 images for hash when tests pass on retry' , async ( ) => {
759+ githubContext . runAttempt = 2 ;
760+ execMock . mockResolvedValue ( 0 ) ;
761+ globMock . mockResolvedValue ( [ 'path/to/screenshots/base.png' ] ) ;
762+ getKeysFromS3Mock . mockResolvedValueOnce ( [
763+ 'new-images/sha/component/new.png'
764+ ] ) ;
765+ getKeysFromS3Mock . mockResolvedValueOnce ( [ ] ) ;
766+ await runAction ( ) ;
767+ expect ( deleteObjectsMock ) . toHaveBeenCalledWith ( {
768+ Bucket : 'some-bucket' ,
769+ Delete : {
770+ Objects : [ { Key : 'new-images/sha/component/new.png' } ] ,
771+ Quiet : true
772+ }
773+ } ) ;
774+ } ) ;
775+
776+ it ( 'should delete S3 images for hash when tests pass on retry with diff-id input' , async ( ) => {
777+ githubContext . runAttempt = 2 ;
778+ getInputMock . mockImplementation ( name => diffIdInputMap [ name ] ) ;
779+ execMock . mockResolvedValue ( 0 ) ;
780+ globMock . mockResolvedValue ( [ 'path/to/screenshots/base.png' ] ) ;
781+ getKeysFromS3Mock . mockResolvedValueOnce ( [
782+ 'new-images/uniqueId/component/new.png'
783+ ] ) ;
784+ getKeysFromS3Mock . mockResolvedValueOnce ( [ ] ) ;
785+ await runAction ( ) ;
786+ expect ( deleteObjectsMock ) . toHaveBeenCalledWith ( {
787+ Bucket : 'some-bucket' ,
788+ Delete : {
789+ Objects : [ { Key : 'new-images/uniqueId/component/new.png' } ] ,
790+ Quiet : true
791+ }
792+ } ) ;
793+ } ) ;
794+
795+ it ( 'should not delete S3 images when tests pass on first attempt' , async ( ) => {
796+ execMock . mockResolvedValue ( 0 ) ;
797+ globMock . mockResolvedValue ( [ 'path/to/screenshots/base.png' ] ) ;
798+ await runAction ( ) ;
799+ expect ( deleteObjectsMock ) . not . toHaveBeenCalled ( ) ;
800+ } ) ;
801+
802+ it ( 'should skip deletion when no images exist in S3 on retry' , async ( ) => {
803+ githubContext . runAttempt = 2 ;
804+ execMock . mockResolvedValue ( 0 ) ;
805+ globMock . mockResolvedValue ( [ 'path/to/screenshots/base.png' ] ) ;
806+ getKeysFromS3Mock . mockResolvedValue ( [ ] ) ;
807+ await runAction ( ) ;
808+ expect ( deleteObjectsMock ) . not . toHaveBeenCalled ( ) ;
809+ } ) ;
810+
753811 it ( 'should call setFailed with the diff message when visual-test-command-fails-on-diff is true' , async ( ) => {
754812 execMock . mockResolvedValue ( 0 ) ;
755813 getBooleanInputMock . mockImplementation ( name =>
0 commit comments