Skip to content

Commit 0dc2f56

Browse files
committed
feat: add cleanup command
1 parent 61acdb5 commit 0dc2f56

5 files changed

Lines changed: 227 additions & 90 deletions

File tree

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- [Ignore Regions](#ignore-regions)
2929
- [CLI Flags](#cli-flags)
3030
- [Updating Snapshots](#updating-snapshots)
31+
- [Cleanup](#cleanup-command)
3132
- [Credits](#credits)
3233

3334
<br />
@@ -274,6 +275,27 @@ There is no "update" command to update the snapshots. If the changes shown in th
274275

275276
<br />
276277

278+
## Cleanup Command
279+
280+
The cleanup command removes all base images which are not used anymore.
281+
This may happen, because you deleted or renamed a test file.
282+
283+
You may add the `--config` option, if your config file is not in the default location.
284+
285+
**Useage:**
286+
287+
```
288+
osnap cleanup
289+
```
290+
291+
or
292+
293+
```
294+
osnap cleanup --config [PATH TO CONFIG]
295+
```
296+
297+
<br />
298+
277299
## Credits
278300

279301
**[ODiff](https://github.com/dmtrKovalenko/odiff):**
@@ -286,8 +308,6 @@ Thank you for your work [@dmtrKovalenko](https://github.com/dmtrKovalenko)!
286308

287309
In decending order of priority (top ones are more important):
288310

289-
- [ ] **Add cleanup command**:
290-
Remove base images which are not used anymore, because the test file was removed.
291311
- [ ] **Listen for network requests**:
292312
Wait for specific network requests to finish, before taking the screenshot. For example: Wait for a failed login attempt to come back, to screenshot the error state
293313
- [ ] **Wait for dom events**:

bin/Main.re

Lines changed: 145 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,114 +6,175 @@ let print_error = msg => {
66
print_endline(<Pastel color=Red> msg </Pastel>);
77
};
88

9-
let main = (noCreate, noOnly, noSkip, config_path) => {
10-
open Lwt_result.Syntax;
11-
let run = {
12-
let* t =
13-
try%lwt(OSnap.setup(~config_path)) {
9+
let config = {
10+
let doc = "
11+
The relative path to the global config file.
12+
";
13+
let default = "";
14+
Arg.(value & opt(file, default) & info(["config"], ~doc));
15+
};
16+
17+
let default_cmd = {
18+
let noCreate = {
19+
let doc = "
20+
With this option enabled, new snapshots will not be created, but fail the whole test run instead.
21+
This option is recommended for ci environments.
22+
";
23+
Arg.(value & flag & info(["no-create"], ~doc));
24+
};
25+
26+
let noOnly = {
27+
let doc = "
28+
With this option enabled, the test run will fail, if you have any test with \"only\" set to true.
29+
This option is recommended for ci environments.
30+
";
31+
Arg.(value & flag & info(["no-only"], ~doc));
32+
};
33+
34+
let noSkip = {
35+
let doc = "
36+
With this option enabled, the test run will fail, if you have any test with \"skip\" set to true.
37+
This option is recommended for ci environments.
38+
";
39+
Arg.(value & flag & info(["no-skip"], ~doc));
40+
};
41+
42+
let exec = (noCreate, noOnly, noSkip, config_path) => {
43+
open Lwt_result.Syntax;
44+
let run = {
45+
let* t =
46+
try%lwt(OSnap.setup(~config_path)) {
47+
| Failure(message) =>
48+
print_error(message);
49+
Lwt_result.fail();
50+
| OSnap_Config.Global.Parse_Error(_) =>
51+
print_error("Your osnap.config.json is in an invalid format.");
52+
Lwt_result.fail();
53+
| OSnap_Config.Global.No_Config_Found =>
54+
print_error("Unable to find a global config file.");
55+
print_error(
56+
"Please create a \"osnap.config.json\" at the root of your project or specifiy the location using the --config option.",
57+
);
58+
Lwt_result.fail();
59+
| OSnap_Config.Test.Duplicate_Tests(tests) =>
60+
print_error(
61+
"Found some tests with duplicate names. Every test has to have a unique name.",
62+
);
63+
print_error("Please rename the following tests: \n");
64+
tests |> List.iter(print_error);
65+
Lwt_result.fail();
66+
| OSnap_Config.Test.Invalid_format =>
67+
print_error("Found some tests with an invalid format.");
68+
Lwt_result.fail();
69+
| exn => raise(exn)
70+
};
71+
72+
let* () =
73+
try%lwt(OSnap.run(t, ~noCreate, ~noOnly, ~noSkip)) {
74+
| Failure(message) =>
75+
print_error(message);
76+
Lwt_result.fail();
77+
| exn => raise(exn)
78+
};
79+
80+
Lwt_result.return();
81+
};
82+
83+
switch (Lwt_main.run(run)) {
84+
| Ok () => 0
85+
| Error () => 1
86+
};
87+
};
88+
89+
(
90+
Term.(const(exec) $ noCreate $ noOnly $ noSkip $ config),
91+
Term.info(
92+
"osnap",
93+
~man=[
94+
`S(Manpage.s_description),
95+
`P(
96+
"OSnap is a snapshot testing tool, which uses chrome to take screenshots and compares them with a base image taken previously.",
97+
),
98+
`P("If both images are equal, the test passes."),
99+
`P(
100+
"If the images aren't equal, the test fails and puts the new image into the \"__updated__\" folder inside of your snapshot folder.
101+
It also generates a new image, which shows the base image (how it looked before), an image with the differing pixels
102+
highlighted and the new image side by side.",
103+
),
104+
`P(
105+
"There is no \"update\" command to update the snapshots. If the changes shown in the diff image are expected,
106+
you just have to move and replace the image from the \"__updated__\" folder into the \"__base_images__\" folder.",
107+
),
108+
],
109+
~exits=
110+
Term.[
111+
exit_info(0, ~doc="on success"),
112+
exit_info(1, ~doc="on failed test runs"),
113+
exit_info(124, ~doc="on command line parsing errors."),
114+
exit_info(125, ~doc="on unexpected internal errors."),
115+
],
116+
),
117+
);
118+
};
119+
120+
let cleanup_cmd = {
121+
let exec = config_path => {
122+
let result =
123+
try(OSnap.cleanup(~config_path)) {
14124
| Failure(message) =>
15125
print_error(message);
16-
Lwt_result.fail();
126+
Result.error();
17127
| OSnap_Config.Global.Parse_Error(_) =>
18128
print_error("Your osnap.config.json is in an invalid format.");
19-
Lwt_result.fail();
129+
Result.error();
20130
| OSnap_Config.Global.No_Config_Found =>
21131
print_error("Unable to find a global config file.");
22132
print_error(
23133
"Please create a \"osnap.config.json\" at the root of your project or specifiy the location using the --config option.",
24134
);
25-
Lwt_result.fail();
135+
Result.error();
26136
| OSnap_Config.Test.Duplicate_Tests(tests) =>
27137
print_error(
28138
"Found some tests with duplicate names. Every test has to have a unique name.",
29139
);
30140
print_error("Please rename the following tests: \n");
31141
tests |> List.iter(print_error);
32-
Lwt_result.fail();
142+
Result.error();
33143
| OSnap_Config.Test.Invalid_format =>
34144
print_error("Found some tests with an invalid format.");
35-
Lwt_result.fail();
36-
| exn => raise(exn)
37-
};
38-
39-
let* () =
40-
try%lwt(OSnap.run(t, ~noCreate, ~noOnly, ~noSkip)) {
41-
| Failure(message) =>
42-
print_error(message);
43-
Lwt_result.fail();
145+
Result.error();
44146
| exn => raise(exn)
45147
};
46148

47-
Lwt_result.return();
149+
switch (result) {
150+
| Ok () => 0
151+
| Error () => 1
152+
};
48153
};
49154

50-
switch (Lwt_main.run(run)) {
51-
| Ok () => 0
52-
| Error () => 1
53-
};
54-
};
55-
56-
let info =
57-
Term.info(
58-
"osnap",
59-
~version="1.0.0",
60-
~man=[
61-
`S(Manpage.s_description),
62-
`P(
63-
"OSnap is a snapshot testing tool, which uses chrome to take screenshots and compares them with a base image taken previously.",
64-
),
65-
`P("If both images are equal, the test passes."),
66-
`P(
67-
"If the images aren't equal, the test fails and puts the new image into the \"__updated__\" folder inside of your snapshot folder.
68-
It also generates a new image, which shows the base image (how it looked before), an image with the differing pixels
69-
highlighted and the new image side by side.",
70-
),
71-
`P(
72-
"There is no \"update\" command to update the snapshots. If the changes shown in the diff image are expected,
73-
you just have to move and replace the image from the \"__updated__\" folder into the \"__base_images__\" folder.",
74-
),
75-
],
76-
~exits=
77-
Term.[
78-
exit_info(0, ~doc="on success"),
79-
exit_info(1, ~doc="on failed test runs"),
80-
exit_info(124, ~doc="on command line parsing errors."),
81-
exit_info(125, ~doc="on unexpected internal errors."),
155+
(
156+
Term.(const(exec) $ config),
157+
Term.info(
158+
"cleanup",
159+
~man=[
160+
`S(Manpage.s_description),
161+
`P(
162+
"
163+
The cleanup command removes all unused base images from the snapshot folder.
164+
This may happen, when a test is removed or renamed.
165+
",
166+
),
82167
],
168+
~exits=
169+
Term.[
170+
exit_info(0, ~doc="on success"),
171+
exit_info(124, ~doc="on command line parsing errors."),
172+
exit_info(125, ~doc="on unexpected internal errors."),
173+
],
174+
),
83175
);
84-
85-
let config = {
86-
let doc = "
87-
The relative path to the global config file.
88-
";
89-
let default = "";
90-
Arg.(value & opt(file, default) & info(["config"], ~doc));
91-
};
92-
93-
let noCreate = {
94-
let doc = "
95-
With this option enabled, new snapshots will not be created, but fail the whole test run instead.
96-
This option is recommended for ci environments.
97-
";
98-
Arg.(value & flag & info(["no-create"], ~doc));
99-
};
100-
101-
let noOnly = {
102-
let doc = "
103-
With this option enabled, the test run will fail, if you have any test with \"only\" set to true.
104-
This option is recommended for ci environments.
105-
";
106-
Arg.(value & flag & info(["no-only"], ~doc));
107-
};
108-
109-
let noSkip = {
110-
let doc = "
111-
With this option enabled, the test run will fail, if you have any test with \"skip\" set to true.
112-
This option is recommended for ci environments.
113-
";
114-
Arg.(value & flag & info(["no-skip"], ~doc));
115176
};
116177

117-
let cmd = Term.(const(main) $ noCreate $ noOnly $ noSkip $ config);
178+
let cmds = [cleanup_cmd];
118179

119-
let () = Term.eval((cmd, info)) |> Term.exit_status;
180+
let () = Term.eval_choice(default_cmd, cmds) |> Term.exit_status;

lib/OSnap.re

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ let read_file_contents = (~path) => {
7777
Lwt.return(data);
7878
};
7979

80+
let get_filename = (name, width, height) =>
81+
Printf.sprintf("/%s_%ix%i.png", name, width, height);
82+
8083
let run = (~noCreate, ~noOnly, ~noSkip, t) => {
8184
let {
8285
browser,
@@ -95,9 +98,6 @@ let run = (~noCreate, ~noOnly, ~noSkip, t) => {
9598
let failed_count = ref(0);
9699
let test_count = ref(0);
97100

98-
let get_filename = (name, width, height) =>
99-
Printf.sprintf("/%s_%ix%i.png", name, width, height);
100-
101101
let all_tests =
102102
tests
103103
|> List.map(test => {
@@ -322,3 +322,57 @@ let run = (~noCreate, ~noOnly, ~noSkip, t) => {
322322
Lwt_result.fail();
323323
};
324324
};
325+
326+
let cleanup = (~config_path) => {
327+
print_newline();
328+
let config = Config.Global.find(~config_path) |> Config.Global.parse;
329+
let (snapshot_dir, _updated_dir, _diff_dir) =
330+
init_folder_structure(config);
331+
let tests = Config.Test.init(config);
332+
333+
let test_file_paths =
334+
tests
335+
|> List.map(test => {
336+
test.Config.Test.sizes
337+
|> List.filter_map(size => {
338+
let (width, height) = size;
339+
let filename = get_filename(test.name, width, height);
340+
let current_image_path = snapshot_dir ++ filename;
341+
let exists = Sys.file_exists(current_image_path);
342+
343+
if (exists) {
344+
Some(current_image_path);
345+
} else {
346+
None;
347+
};
348+
})
349+
})
350+
|> List.flatten;
351+
352+
let files_to_delete =
353+
FileUtil.ls(snapshot_dir)
354+
|> List.find_all(file => !List.mem(file, test_file_paths));
355+
let num_files_to_delete = List.length(files_to_delete);
356+
357+
if (num_files_to_delete > 0) {
358+
print_endline(
359+
Pastel.bold @@
360+
Printf.sprintf("Deleting %i files...\n", num_files_to_delete),
361+
);
362+
files_to_delete
363+
|> List.iter(file => {
364+
FileUtil.rm([file]);
365+
print_endline(Pastel.dim @@ Printf.sprintf("Deleted %s", file));
366+
});
367+
368+
print_endline(Pastel.bold @@ Pastel.green @@ "\nDone!");
369+
} else {
370+
print_endline(
371+
Pastel.bold @@ Pastel.green @@ "Everything clean. No files to remove!",
372+
);
373+
};
374+
375+
print_newline();
376+
377+
Result.ok();
378+
};

lib/OSnap.rei

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ type t = {
1010

1111
let setup: (~config_path: string) => Lwt_result.t(t, 'a);
1212

13+
let cleanup: (~config_path: string) => Result.t(unit, unit);
14+
1315
let run:
1416
(~noCreate: bool, ~noOnly: bool, ~noSkip: bool, t) =>
1517
Lwt_result.t(unit, unit);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@space-labs/osnap",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "The speedy and easy to use snapshot testing tool for your project!",
55
"license": "MIT",
66
"repository": {

0 commit comments

Comments
 (0)