Skip to content

Commit d9b0223

Browse files
committed
Merge branch 'feature/tesseract'
2 parents 5e4d1d6 + dac0908 commit d9b0223

File tree

138 files changed

+35383
-207
lines changed

Some content is hidden

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

138 files changed

+35383
-207
lines changed

ACKNOWLEDGMENTS.md

Lines changed: 624 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ A lightweight macOS menu bar app that captures screenshots of tables and convert
44

55
![macOS](https://img.shields.io/badge/macOS-12.3+-blue.svg)
66
![Swift](https://img.shields.io/badge/Swift-5.5+-orange.svg)
7-
![License](https://img.shields.io/badge/license-MIT-green.svg)
7+
![License](https://img.shields.io/badge/license-GPL--3.0-blue.svg)
88

99

1010
### Installing the App
@@ -19,9 +19,54 @@ Since this app is not signed with a paid Developer ID certificate, macOS will sh
1919
5. Grant Screen Recording permission in System Settings
2020

2121
After the first launch, you can open it normally.
22-
22+
23+
## Features
24+
25+
- **Menu Bar App**: Lightweight application that runs from the macOS menu bar
26+
- **Interactive Screenshot**: Uses native macOS screenshot tool for precise table selection
27+
- **Dual OCR Support**:
28+
- Primary: Apple Vision framework for fast, accurate text recognition
29+
- Fallback: Tesseract OCR for challenging cases (e.g., single letters)
30+
- **Multiple Output Formats**: Export as CSV or Markdown
31+
- **Clipboard Integration**: Automatically copies result to clipboard for easy pasting
32+
33+
## License & Third-Party Acknowledgments
34+
35+
This project is licensed under the **GNU General Public License v3.0 (GPL-3.0)**. See [LICENSE.md](LICENSE.md) for details.
36+
37+
**Third-Party Libraries**: This application uses several open-source libraries for OCR functionality, including Tesseract OCR, Leptonica, and various image processing libraries. See [ACKNOWLEDGMENTS.md](ACKNOWLEDGMENTS.md) for complete license information and attributions.
38+
39+
⚠️ **Note**: The project includes LibJPEG which has a license (IJG) that may not be fully compatible with GPL v3. See ACKNOWLEDGMENTS.md for details and recommended solutions before commercial distribution.
40+
2341
## Development
2442

43+
44+
### Running tests
45+
46+
#### Run all tests:
47+
48+
```bash
49+
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture -destination 'platform=macOS,arch=arm64'
50+
```
51+
52+
#### Run only the ComplexLayoutMultiColMultiRowTests:
53+
54+
```bash
55+
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture -destination 'platform=macOS,arch=arm64' -only-testing:TableCaptureTests/ComplexLayoutMultiColMultiRowTests
56+
```
57+
58+
#### Run only the debug test to see the OCR output:
59+
60+
```bash
61+
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture -destination 'platform=macOS,arch=arm64' -only-testing:TableCaptureTests/ComplexLayoutMultiColMultiRowTests/debugComplexLayout
62+
```
63+
64+
#### Run a specific test (CSV or Markdown):
65+
66+
```bash
67+
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture -destination 'platform=macOS,arch=arm64' -only-testing:TableCaptureTests/ComplexLayoutMultiColMultiRowTests/testComplexLayoutMarkdown
68+
```
69+
2570
### Building a Release
2671

2772
#### Creating a Release Candidate
@@ -30,17 +75,32 @@ After the first launch, you can open it normally.
3075
- Select **Product → Archive** from the menu
3176
- Wait for the archive to complete
3277
- In the Organizer window that appears, click **Distribute App**
78+
- Choose **Custom**
3379
- Choose **Copy App** and save it to a location (e.g., Desktop)
3480

3581
2. **Locate the .app Bundle**
3682
- Find `TableCapture.app` in the exported location
3783

38-
3. **Create a DMG Installer**
84+
3. **Create a Professional DMG Installer** (with drag-to-Applications)
85+
3986
```bash
40-
hdiutil create -volname "TableCapture" -srcfolder TableCapture.app -ov -format UDZO TableCapture.dmg
87+
# Create Applications symlink next to your app
88+
ln -s /Applications Applications
89+
90+
# Create the DMG containing both the app and Applications link
91+
# (saves to parent directory to avoid including the DMG in itself)
92+
hdiutil create -volname "TableCapture" \
93+
-srcfolder . \
94+
-ov -format UDZO \
95+
../TableCapture.dmg
96+
97+
# Clean up the symlink
98+
rm Applications
4199
```
42-
43-
This creates a compressed disk image that users can download and mount.
100+
101+
**Done!** Your `TableCapture.dmg` now has the professional drag-to-Applications interface.
102+
103+
When users open the DMG, they'll see your app and an Applications folder - they just drag the app to Applications to install.
44104

45105
4. **Create a GitHub Release**
46106
- Go to your repository → **Releases**

TESTING.md

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
# Testing Guide for TableCapture
2+
3+
This guide explains how to write and run tests for the TableCapture app.
4+
5+
## Test Structure
6+
7+
The project uses two types of tests:
8+
9+
### 1. **Unit Tests** (`TableCaptureTests/`)
10+
- **Framework:** Swift Testing (modern, introduced in Swift 5.9+)
11+
- **Purpose:** Test individual functions, logic, and data transformations
12+
- **Speed:** Fast (no UI, no app launch)
13+
- **Location:** `TableCaptureTests/TableCaptureTests.swift`
14+
15+
### 2. **UI Tests** (`TableCaptureUITests/`)
16+
- **Framework:** XCTest + XCUITest
17+
- **Purpose:** Test user interactions and full app behavior
18+
- **Speed:** Slower (launches full app)
19+
- **Location:** `TableCaptureUITests/TableCaptureUITests.swift`
20+
21+
---
22+
23+
## Running Tests
24+
25+
### In Xcode
26+
27+
**Run all tests:**
28+
- Press `⌘U` (Command + U)
29+
- Or: **Product → Test**
30+
31+
**Run a single test:**
32+
1. Click the diamond icon next to the test function
33+
2. Or: Put cursor in test function and press `⌘U`
34+
35+
**Run a specific test file:**
36+
- Click the diamond icon next to the `struct` or `class` name
37+
38+
### From Command Line
39+
40+
```bash
41+
# Run all tests
42+
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture
43+
44+
# Run only unit tests
45+
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture -only-testing:TableCaptureTests
46+
47+
# Run only UI tests
48+
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture -only-testing:TableCaptureUITests
49+
```
50+
51+
---
52+
53+
## Writing Unit Tests (Swift Testing)
54+
55+
### Basic Structure
56+
57+
```swift
58+
import Testing
59+
@testable import TableCapture
60+
61+
struct MyTests {
62+
@Test("Description of what this tests")
63+
func testSomething() async throws {
64+
// Arrange: Set up test data
65+
let input = "test"
66+
67+
// Act: Perform the action
68+
let result = someFunction(input)
69+
70+
// Assert: Verify the result
71+
#expect(result == "expected")
72+
}
73+
}
74+
```
75+
76+
### Key Features
77+
78+
**Assertions:**
79+
```swift
80+
#expect(value == expected) // Basic equality
81+
#expect(value != unwanted) // Inequality
82+
#expect(array.contains(item)) // Contains check
83+
#expect(value > 0) // Comparison
84+
#expect(value == nil) // Nil check
85+
```
86+
87+
**Async tests:**
88+
```swift
89+
@Test func testAsync() async throws {
90+
let result = await someAsyncFunction()
91+
#expect(result.isSuccess)
92+
}
93+
```
94+
95+
**Test with parameters:**
96+
```swift
97+
@Test(arguments: [1, 2, 3, 4, 5])
98+
func testMultipleInputs(number: Int) {
99+
#expect(number > 0)
100+
}
101+
```
102+
103+
---
104+
105+
## Writing UI Tests (XCTest)
106+
107+
### Basic Structure
108+
109+
```swift
110+
import XCTest
111+
112+
final class MyUITests: XCTestCase {
113+
var app: XCUIApplication!
114+
115+
override func setUpWithError() throws {
116+
continueAfterFailure = false
117+
app = XCUIApplication()
118+
app.launch()
119+
}
120+
121+
@MainActor
122+
func testSomething() throws {
123+
// Find UI elements
124+
let button = app.buttons["My Button"]
125+
126+
// Interact with them
127+
button.tap()
128+
129+
// Verify results
130+
XCTAssertTrue(button.exists)
131+
}
132+
}
133+
```
134+
135+
### Key Features
136+
137+
**Finding elements:**
138+
```swift
139+
app.buttons["Button Title"] // Button by label
140+
app.textFields["Email"] // Text field
141+
app.windows["Window Title"] // Window
142+
app.staticTexts["Label"] // Text label
143+
```
144+
145+
**Interactions:**
146+
```swift
147+
element.tap() // Click
148+
element.typeText("hello") // Type text
149+
element.swipeLeft() // Swipe gesture
150+
```
151+
152+
**Assertions:**
153+
```swift
154+
XCTAssertTrue(element.exists)
155+
XCTAssertEqual(element.label, "Expected")
156+
XCTAssertFalse(element.isEnabled)
157+
XCTAssertNotNil(value)
158+
```
159+
160+
---
161+
162+
## Current Test Coverage
163+
164+
### Unit Tests (`TableCaptureTests.swift`)
165+
166+
**CSV Formatting:**
167+
- ✅ Escaping commas
168+
- ✅ Escaping quotes
169+
- ✅ Handling empty cells
170+
171+
**Markdown Formatting:**
172+
- ✅ Table structure (headers, separators)
173+
- ✅ Escaping pipe characters
174+
- ✅ Handling uneven row lengths
175+
176+
**Grid Management:**
177+
- ✅ Adding columns/rows
178+
- ✅ Removing selected lines
179+
- ✅ Clearing all lines
180+
181+
### UI Tests (`TableCaptureUITests.swift`)
182+
183+
**App Launch:**
184+
- ✅ Launches without crashing
185+
- ✅ Stays running after launch
186+
187+
**Performance:**
188+
- ✅ Launch time measurement
189+
- ✅ Memory usage measurement
190+
191+
**Accessibility:**
192+
- ✅ VoiceOver support checks
193+
194+
---
195+
196+
## Best Practices
197+
198+
### Unit Tests
199+
200+
1. **Keep tests isolated** - Each test should be independent
201+
2. **Use descriptive names** - Test names should explain what they verify
202+
3. **Test one thing** - Each test should verify a single behavior
203+
4. **Use `@testable import`** - Access internal types for testing
204+
205+
### UI Tests
206+
207+
1. **Use accessibility identifiers** - Make elements easier to find
208+
2. **Wait for elements** - Use `waitForExistence(timeout:)`
209+
3. **Test user journeys** - Simulate real user workflows
210+
4. **Keep tests stable** - Avoid flaky tests with proper waits
211+
212+
### General
213+
214+
1. **Run tests before commits** - Ensure nothing breaks
215+
2. **Write tests for bugs** - Prevent regressions
216+
3. **Test edge cases** - Empty strings, nil values, extreme inputs
217+
4. **Keep tests maintainable** - Refactor test code too
218+
219+
---
220+
221+
## Adding New Tests
222+
223+
### For a new function in `TableEditorViewModel`:
224+
225+
```swift
226+
@Test("What this function should do")
227+
func testNewFunction() async throws {
228+
let viewModel = TableEditorViewModel(image: createTestImage())
229+
230+
// Test your function
231+
viewModel.newFunction()
232+
233+
#expect(viewModel.someProperty == expectedValue)
234+
}
235+
```
236+
237+
### For a new UI feature:
238+
239+
```swift
240+
@MainActor
241+
func testNewUIFeature() throws {
242+
let app = XCUIApplication()
243+
app.launch()
244+
245+
// Find and interact with your UI
246+
let newButton = app.buttons["New Feature"]
247+
newButton.tap()
248+
249+
// Verify the result
250+
XCTAssertTrue(app.staticTexts["Success"].exists)
251+
}
252+
```
253+
254+
---
255+
256+
## Troubleshooting
257+
258+
**Tests won't run:**
259+
- Clean build folder: `⌘⇧K`
260+
- Delete derived data: `Xcode → Settings → Locations → Derived Data`
261+
262+
**UI tests can't find elements:**
263+
- Add accessibility identifiers to SwiftUI views:
264+
```swift
265+
Button("My Button") { }
266+
.accessibilityIdentifier("myButton")
267+
```
268+
- Use `po app.debugDescription` in debugger to see all elements
269+
270+
**Tests are flaky:**
271+
- Add explicit waits:
272+
```swift
273+
let element = app.buttons["My Button"]
274+
XCTAssertTrue(element.waitForExistence(timeout: 5))
275+
```
276+
277+
---
278+
279+
## Resources
280+
281+
- [Swift Testing Documentation](https://developer.apple.com/documentation/testing)
282+
- [XCTest Documentation](https://developer.apple.com/documentation/xctest)
283+
- [UI Testing Guide](https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/09-ui_testing.html)

0 commit comments

Comments
 (0)