Skip to content

Commit 8bcbb35

Browse files
committed
Add Tests
1 parent 6486090 commit 8bcbb35

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+8536
-9
lines changed

CLAUDE.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,10 @@ npm run clean:l10n # Clean only translations directory
9898

9999
```bash
100100
# Build Swift package
101-
make build-swift-package
102-
103-
# Build Swift package (force refresh of npm deps/translations if needed)
104-
make build-swift-package REFRESH_DEPS=1 REFRESH_L10N=1
101+
swift build
105102

106103
# Run Swift tests
107-
make test-swift-package
104+
swift test
108105
```
109106

110107
### Android Development

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ test-js-watch: npm-dependencies ## Run JavaScript tests in watch mode
143143
test-swift-package: build ## Run Swift package tests
144144
$(call XCODEBUILD_CMD, test)
145145

146+
.PHONY: test-swift-package-logic
147+
test-swift-package-logic:
148+
$(call XCODEBUILD_LOGIC_CMD, test)
149+
146150
.PHONY: test-android
147151
test-android: ## Run Android tests
148152
@echo "--- :android: Running Android Tests"

Package.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ let package = Package(
2424
.testTarget(
2525
name: "GutenbergKitTests",
2626
dependencies: ["GutenbergKit"],
27-
path: "ios/Tests",
27+
path: "ios/Tests/GutenbergKitTests",
2828
exclude: [],
2929
resources: [
30-
.process("GutenbergKitTests/Resources/")
30+
.process("Resources")
3131
]
32-
),
32+
)
3333
]
3434
)
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
import Foundation
2+
import Testing
3+
4+
@testable import GutenbergKit
5+
6+
@Suite
7+
struct FileManagerExtensionsTests {
8+
9+
// MARK: - fileExists(at:)
10+
@Test("fileExists(at:) returns true for existing file")
11+
func fileExistsReturnsTrueForExistingFile() throws {
12+
let tempFile = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
13+
try Data("test".utf8).write(to: tempFile)
14+
defer { try? FileManager.default.removeItem(at: tempFile) }
15+
16+
#expect(FileManager.default.fileExists(at: tempFile) == true)
17+
}
18+
19+
@Test("fileExists(at:) returns false for non-existing file")
20+
func fileExistsReturnsFalseForNonExistingFile() {
21+
let nonExistentFile = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
22+
#expect(FileManager.default.fileExists(at: nonExistentFile) == false)
23+
}
24+
25+
@Test("fileExists(at:) returns true for existing directory")
26+
func fileExistsReturnsTrueForDirectory() throws {
27+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
28+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
29+
defer { try? FileManager.default.removeItem(at: tempDir) }
30+
31+
#expect(FileManager.default.fileExists(at: tempDir) == true)
32+
}
33+
34+
// MARK: - directoryExists(at:)
35+
@Test("directoryExists(at:) returns true for existing directory")
36+
func directoryExistsReturnsTrueForDirectory() throws {
37+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
38+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
39+
defer { try? FileManager.default.removeItem(at: tempDir) }
40+
41+
#expect(FileManager.default.directoryExists(at: tempDir) == true)
42+
}
43+
44+
@Test("directoryExists(at:) returns false for non-existing path")
45+
func directoryExistsReturnsFalseForNonExisting() {
46+
let nonExistentDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
47+
#expect(FileManager.default.directoryExists(at: nonExistentDir) == false)
48+
}
49+
50+
@Test("directoryExists(at:) returns false for file")
51+
func directoryExistsReturnsFalseForFile() throws {
52+
let tempFile = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
53+
try Data("test".utf8).write(to: tempFile)
54+
defer { try? FileManager.default.removeItem(at: tempFile) }
55+
56+
#expect(FileManager.default.directoryExists(at: tempFile) == false)
57+
}
58+
59+
@Test("directoryExists(at:) distinguishes files from directories")
60+
func directoryExistsDistinguishesFilesFromDirectories() throws {
61+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
62+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
63+
64+
let tempFile = tempDir.appending(path: "file.txt")
65+
try Data("test".utf8).write(to: tempFile)
66+
67+
defer { try? FileManager.default.removeItem(at: tempDir) }
68+
69+
#expect(FileManager.default.directoryExists(at: tempDir) == true)
70+
#expect(FileManager.default.directoryExists(at: tempFile) == false)
71+
#expect(FileManager.default.fileExists(at: tempFile) == true)
72+
}
73+
74+
// MARK: - Percent Encoding Tests
75+
76+
@Test("fileExists(at:) handles paths with spaces")
77+
func fileExistsHandlesPathsWithSpaces() throws {
78+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
79+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
80+
defer { try? FileManager.default.removeItem(at: tempDir) }
81+
82+
let fileWithSpaces = tempDir.appending(path: "file with spaces.txt")
83+
try Data("test".utf8).write(to: fileWithSpaces)
84+
85+
#expect(FileManager.default.fileExists(at: fileWithSpaces) == true)
86+
}
87+
88+
@Test("fileExists(at:) handles paths with percent-encoded characters")
89+
func fileExistsHandlesPercentEncodedPaths() throws {
90+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
91+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
92+
defer { try? FileManager.default.removeItem(at: tempDir) }
93+
94+
// Create a file with special characters that would be percent-encoded in URLs
95+
let specialFileName = "file%20with%20encoded.txt"
96+
let fileWithSpecialChars = tempDir.appending(path: specialFileName)
97+
try Data("test".utf8).write(to: fileWithSpecialChars)
98+
99+
#expect(FileManager.default.fileExists(at: fileWithSpecialChars) == true)
100+
}
101+
102+
@Test("fileExists(at:) handles paths with unicode characters")
103+
func fileExistsHandlesUnicodePaths() throws {
104+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
105+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
106+
defer { try? FileManager.default.removeItem(at: tempDir) }
107+
108+
let unicodeFileName = "文件名.txt"
109+
let fileWithUnicode = tempDir.appending(path: unicodeFileName)
110+
try Data("test".utf8).write(to: fileWithUnicode)
111+
112+
#expect(FileManager.default.fileExists(at: fileWithUnicode) == true)
113+
}
114+
115+
@Test("fileExists(at:) handles paths with special URL characters")
116+
func fileExistsHandlesSpecialURLCharacters() throws {
117+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
118+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
119+
defer { try? FileManager.default.removeItem(at: tempDir) }
120+
121+
// Characters that have special meaning in URLs: # ? & =
122+
let specialFileName = "file#with?special&chars=test.txt"
123+
let fileWithSpecialChars = tempDir.appending(path: specialFileName)
124+
try Data("test".utf8).write(to: fileWithSpecialChars)
125+
126+
#expect(FileManager.default.fileExists(at: fileWithSpecialChars) == true)
127+
}
128+
129+
@Test("directoryExists(at:) handles paths with spaces")
130+
func directoryExistsHandlesPathsWithSpaces() throws {
131+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
132+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
133+
defer { try? FileManager.default.removeItem(at: tempDir) }
134+
135+
let dirWithSpaces = tempDir.appending(path: "directory with spaces")
136+
try FileManager.default.createDirectory(at: dirWithSpaces, withIntermediateDirectories: true)
137+
138+
#expect(FileManager.default.directoryExists(at: dirWithSpaces) == true)
139+
}
140+
141+
@Test("directoryExists(at:) handles paths with unicode characters")
142+
func directoryExistsHandlesUnicodePaths() throws {
143+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
144+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
145+
defer { try? FileManager.default.removeItem(at: tempDir) }
146+
147+
let unicodeDirName = "目录名"
148+
let dirWithUnicode = tempDir.appending(path: unicodeDirName)
149+
try FileManager.default.createDirectory(at: dirWithUnicode, withIntermediateDirectories: true)
150+
151+
#expect(FileManager.default.directoryExists(at: dirWithUnicode) == true)
152+
}
153+
154+
@Test("directoryExists(at:) handles nested paths with special characters")
155+
func directoryExistsHandlesNestedSpecialPaths() throws {
156+
let tempDir = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
157+
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
158+
defer { try? FileManager.default.removeItem(at: tempDir) }
159+
160+
let nestedDir =
161+
tempDir
162+
.appending(path: "level 1 with spaces")
163+
.appending(path: "level2#special")
164+
.appending(path: "レベル3")
165+
try FileManager.default.createDirectory(at: nestedDir, withIntermediateDirectories: true)
166+
167+
#expect(FileManager.default.directoryExists(at: nestedDir) == true)
168+
}
169+
170+
@Test(
171+
"fileExists(at:) correctly reports non-existence for percent-encoded path that doesn't exist")
172+
func fileExistsReportsNonExistenceForEncodedPath() {
173+
let nonExistentPath = FileManager.default.temporaryDirectory
174+
.appending(path: UUID().uuidString)
175+
.appending(path: "file with spaces.txt")
176+
177+
#expect(FileManager.default.fileExists(at: nonExistentPath) == false)
178+
}
179+
}
180+
181+
// MARK: - URL.appending(rawPath:) Tests
182+
183+
@Suite
184+
struct URLAppendingRawPathTests {
185+
186+
// MARK: - Basic Path Appending
187+
188+
@Test("appends path when base URL has no trailing slash and path has no leading slash")
189+
func appendsPathWithNoSlashes() {
190+
let base = URL(string: "https://example.com/api")!
191+
let result = base.appending(rawPath: "posts")
192+
#expect(result.absoluteString == "https://example.com/api/posts")
193+
}
194+
195+
@Test("appends path when base URL has trailing slash and path has no leading slash")
196+
func appendsPathWhenBaseHasTrailingSlash() {
197+
let base = URL(string: "https://example.com/api/")!
198+
let result = base.appending(rawPath: "posts")
199+
#expect(result.absoluteString == "https://example.com/api/posts")
200+
}
201+
202+
@Test("appends path when base URL has no trailing slash and path has leading slash")
203+
func appendsPathWhenPathHasLeadingSlash() {
204+
let base = URL(string: "https://example.com/api")!
205+
let result = base.appending(rawPath: "/posts")
206+
#expect(result.absoluteString == "https://example.com/api/posts")
207+
}
208+
209+
@Test("appends path when both have slashes (avoids double slash)")
210+
func appendsPathAvoidingDoubleSlash() {
211+
let base = URL(string: "https://example.com/api/")!
212+
let result = base.appending(rawPath: "/posts")
213+
#expect(result.absoluteString == "https://example.com/api/posts")
214+
}
215+
216+
// MARK: - Multi-segment Paths
217+
218+
@Test("appends multi-segment path")
219+
func appendsMultiSegmentPath() {
220+
let base = URL(string: "https://example.com/wp-json")!
221+
let result = base.appending(rawPath: "wp/v2/posts")
222+
#expect(result.absoluteString == "https://example.com/wp-json/wp/v2/posts")
223+
}
224+
225+
@Test("appends multi-segment path with leading slash")
226+
func appendsMultiSegmentPathWithLeadingSlash() {
227+
let base = URL(string: "https://example.com/wp-json")!
228+
let result = base.appending(rawPath: "/wp/v2/posts")
229+
#expect(result.absoluteString == "https://example.com/wp-json/wp/v2/posts")
230+
}
231+
232+
// MARK: - Query Parameters
233+
234+
@Test("appends path with query parameters")
235+
func appendsPathWithQueryParameters() {
236+
let base = URL(string: "https://example.com/api")!
237+
let result = base.appending(rawPath: "posts?context=edit")
238+
#expect(result.absoluteString == "https://example.com/api/posts?context=edit")
239+
}
240+
241+
@Test("appends path with multiple query parameters")
242+
func appendsPathWithMultipleQueryParameters() {
243+
let base = URL(string: "https://example.com/api")!
244+
let result = base.appending(rawPath: "posts?context=edit&per_page=10")
245+
#expect(result.absoluteString == "https://example.com/api/posts?context=edit&per_page=10")
246+
}
247+
248+
@Test("preserves query parameters in base URL when appending path")
249+
func preservesBaseQueryParameters() {
250+
let base = URL(string: "https://example.com/api?token=abc")!
251+
let result = base.appending(rawPath: "posts")
252+
#expect(result.absoluteString == "https://example.com/api?token=abc/posts")
253+
}
254+
255+
// MARK: - Special Characters
256+
257+
@Test("appends path with numeric ID")
258+
func appendsPathWithNumericId() {
259+
let base = URL(string: "https://example.com/api/posts")!
260+
let result = base.appending(rawPath: "123")
261+
#expect(result.absoluteString == "https://example.com/api/posts/123")
262+
}
263+
264+
@Test("appends path with hyphens and underscores")
265+
func appendsPathWithHyphensAndUnderscores() {
266+
let base = URL(string: "https://example.com")!
267+
let result = base.appending(rawPath: "wp-block-editor/v1/settings_options")
268+
#expect(result.absoluteString == "https://example.com/wp-block-editor/v1/settings_options")
269+
}
270+
271+
// MARK: - Edge Cases
272+
273+
@Test("appends empty path")
274+
func appendsEmptyPath() {
275+
let base = URL(string: "https://example.com/api")!
276+
let result = base.appending(rawPath: "")
277+
#expect(result.absoluteString == "https://example.com/api/")
278+
}
279+
280+
@Test("appends single slash")
281+
func appendsSingleSlash() {
282+
let base = URL(string: "https://example.com/api")!
283+
let result = base.appending(rawPath: "/")
284+
#expect(result.absoluteString == "https://example.com/api/")
285+
}
286+
287+
@Test("works with root URL")
288+
func worksWithRootUrl() {
289+
let base = URL(string: "https://example.com")!
290+
let result = base.appending(rawPath: "api/posts")
291+
#expect(result.absoluteString == "https://example.com/api/posts")
292+
}
293+
294+
@Test("works with root URL with trailing slash")
295+
func worksWithRootUrlWithTrailingSlash() {
296+
let base = URL(string: "https://example.com/")!
297+
let result = base.appending(rawPath: "api/posts")
298+
#expect(result.absoluteString == "https://example.com/api/posts")
299+
}
300+
301+
// MARK: - Real-world WordPress API Paths
302+
303+
@Test("appends WordPress post endpoint path")
304+
func appendsWordPressPostEndpoint() {
305+
let base = URL(string: "https://example.com/wp-json")!
306+
let result = base.appending(rawPath: "wp/v2/posts/42?context=edit")
307+
#expect(result.absoluteString == "https://example.com/wp-json/wp/v2/posts/42?context=edit")
308+
}
309+
310+
@Test("appends WordPress block editor settings path")
311+
func appendsWordPressBlockEditorSettingsPath() {
312+
let base = URL(string: "https://example.com/wp-json")!
313+
let result = base.appending(rawPath: "wp-block-editor/v1/settings")
314+
#expect(result.absoluteString == "https://example.com/wp-json/wp-block-editor/v1/settings")
315+
}
316+
317+
@Test("appends WordPress themes endpoint path")
318+
func appendsWordPressThemesEndpoint() {
319+
let base = URL(string: "https://example.com/wp-json")!
320+
let result = base.appending(rawPath: "wp/v2/themes?status=active")
321+
#expect(result.absoluteString == "https://example.com/wp-json/wp/v2/themes?status=active")
322+
}
323+
}

0 commit comments

Comments
 (0)