| @@ -0,0 +1,131 @@ |
| |
1 | import Foundation |
| |
2 | import Testing |
| |
3 | @testable import gitbay |
| |
4 | |
| |
5 | struct IssueFilterTests { |
| |
6 | |
| |
7 | @Test func theDefaultIsOpenAndNothingElse() { |
| |
8 | #expect(IssueFilter().flags() == ["--state", "open"]) |
| |
9 | #expect(IssueFilter().isNarrowed == false) |
| |
10 | } |
| |
11 | |
| |
12 | @Test func stateIsAlwaysFirst() { |
| |
13 | var f = IssueFilter() |
| |
14 | f.state = .closed |
| |
15 | f.author = "cmc" |
| |
16 | #expect(f.flags().prefix(2) == ["--state", "closed"]) |
| |
17 | } |
| |
18 | |
| |
19 | @Test func eachFieldRendersItsOwnFlag() { |
| |
20 | var f = IssueFilter() |
| |
21 | f.label = "bug" |
| |
22 | f.assignee = "cmc" |
| |
23 | f.author = "rae" |
| |
24 | f.milestone = "v1.0.0" |
| |
25 | f.search = "crash on launch" |
| |
26 | #expect(f.flags() == [ |
| |
27 | "--state", "open", |
| |
28 | "--search", "crash on launch", |
| |
29 | "--label", "bug", |
| |
30 | "--assignee", "cmc", |
| |
31 | "--author", "rae", |
| |
32 | "--milestone", "v1.0.0", |
| |
33 | ]) |
| |
34 | #expect(f.isNarrowed) |
| |
35 | } |
| |
36 | |
| |
37 | /// An empty value must send no flag. `--search ""` is a different |
| |
38 | /// query from no search at all. |
| |
39 | @Test func emptyAndWhitespaceValuesSendNoFlag() { |
| |
40 | var f = IssueFilter() |
| |
41 | f.search = " " |
| |
42 | f.label = "" |
| |
43 | f.assignee = nil |
| |
44 | f.author = " " |
| |
45 | f.milestone = "" |
| |
46 | #expect(f.flags() == ["--state", "open"]) |
| |
47 | #expect(f.isNarrowed == false) |
| |
48 | } |
| |
49 | |
| |
50 | /// "none" is a real milestone filter meaning "has no milestone". |
| |
51 | @Test func milestoneNoneIsAValueNotAnAbsence() { |
| |
52 | var f = IssueFilter() |
| |
53 | f.milestone = "none" |
| |
54 | #expect(f.flags() == ["--state", "open", "--milestone", "none"]) |
| |
55 | #expect(f.isNarrowed) |
| |
56 | } |
| |
57 | |
| |
58 | /// The server quotes FTS5 terms itself, so an operator is a word to |
| |
59 | /// match. The app must not escape or reject it. |
| |
60 | @Test func searchTextIsPassedThroughUnquoted() { |
| |
61 | var f = IssueFilter() |
| |
62 | f.search = "c++ AND \"quoted" |
| |
63 | #expect(f.flags() == ["--state", "open", "--search", "c++ AND \"quoted"]) |
| |
64 | } |
| |
65 | |
| |
66 | @Test func surroundingWhitespaceIsTrimmedFromValues() { |
| |
67 | var f = IssueFilter() |
| |
68 | f.search = " crash " |
| |
69 | f.label = " bug " |
| |
70 | #expect(f.flags() == ["--state", "open", "--search", "crash", "--label", "bug"]) |
| |
71 | } |
| |
72 | |
| |
73 | @Test func theEmptyMessageNamesTheNarrowing() { |
| |
74 | #expect(IssueFilter().emptyMessage == "No open issues.") |
| |
75 | var all = IssueFilter() |
| |
76 | all.state = .all |
| |
77 | #expect(all.emptyMessage == "No issues.") |
| |
78 | var narrowed = IssueFilter() |
| |
79 | narrowed.label = "bug" |
| |
80 | #expect(narrowed.emptyMessage == "No open issues match these filters.") |
| |
81 | } |
| |
82 | } |
| |
83 | |
| |
84 | struct MRFilterTests { |
| |
85 | |
| |
86 | @Test func theDefaultIsOpenAndNothingElse() { |
| |
87 | #expect(MRFilter().flags() == ["--state", "open"]) |
| |
88 | #expect(MRFilter().isNarrowed == false) |
| |
89 | } |
| |
90 | |
| |
91 | @Test func eachFieldRendersItsOwnFlag() { |
| |
92 | var f = MRFilter() |
| |
93 | f.author = "cmc" |
| |
94 | f.milestone = "v1.0.0" |
| |
95 | f.search = "retarget" |
| |
96 | #expect(f.flags() == [ |
| |
97 | "--state", "open", |
| |
98 | "--search", "retarget", |
| |
99 | "--author", "cmc", |
| |
100 | "--milestone", "v1.0.0", |
| |
101 | ]) |
| |
102 | } |
| |
103 | |
| |
104 | /// `mr list` accepts source_gone; the spelling is the wire spelling, |
| |
105 | /// not the Swift case name. |
| |
106 | @Test func sourceGoneSpellsItselfTheWayTheCommandDoes() { |
| |
107 | var f = MRFilter() |
| |
108 | f.state = .sourceGone |
| |
109 | #expect(f.flags() == ["--state", "source_gone"]) |
| |
110 | #expect(MRFilter.State.sourceGone.label == "Source gone") |
| |
111 | } |
| |
112 | |
| |
113 | @Test func everyStateTheCommandAcceptsIsOffered() { |
| |
114 | #expect(MRFilter.State.allCases.map(\.rawValue) |
| |
115 | == ["open", "merged", "closed", "source_gone", "all"]) |
| |
116 | } |
| |
117 | |
| |
118 | @Test func emptyValuesSendNoFlag() { |
| |
119 | var f = MRFilter() |
| |
120 | f.search = " " |
| |
121 | f.author = "" |
| |
122 | #expect(f.flags() == ["--state", "open"]) |
| |
123 | } |
| |
124 | |
| |
125 | @Test func theEmptyMessageNamesTheNarrowing() { |
| |
126 | #expect(MRFilter().emptyMessage == "No open merge requests.") |
| |
127 | var narrowed = MRFilter() |
| |
128 | narrowed.search = "x" |
| |
129 | #expect(narrowed.emptyMessage == "No open merge requests match these filters.") |
| |
130 | } |
| |
131 | } |