diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f92afe7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +[*.swift] +indent_style = space +indent_size = 4 +tab_width = 4 +end_of_line = crlf +insert_final_newline = false +max_line_length = 120 +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.github/workflows/command.yml b/.github/workflows/command.yml index 4de4daf..885606f 100644 --- a/.github/workflows/command.yml +++ b/.github/workflows/command.yml @@ -24,6 +24,6 @@ jobs: run: | set -o pipefail && \ xcodebuild -scheme Csv2ImgCmd \ - clean build test \ + clean build \ -destination 'platform=OS X,arch=arm64' \ | xcbeautify diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Csv2ImgCmd.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Csv2ImgCmd.xcscheme index 4a49729..093e331 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/Csv2ImgCmd.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/Csv2ImgCmd.xcscheme @@ -43,7 +43,7 @@ shouldUseLaunchSchemeArgsEnv = "YES"> + skipped = "YES"> ? diff --git a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+iOS.swift b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+iOS.swift index 76d3a0c..0b0e559 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+iOS.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+iOS.swift @@ -27,47 +27,61 @@ import SwiftUI NavigationView { Form { Section(header: Text("Export Settings")) { - Picker("Export Type", selection: Binding( - get: { model.state.exportType }, - set: { model.update(keyPath: \.exportType, value: $0) } - )) { + Picker( + "Export Type", + selection: Binding( + get: { model.state.exportType }, + set: { model.update(keyPath: \.exportType, value: $0) } + ) + ) { Text("PDF").tag(Csv.ExportType.pdf) Text("PNG").tag(Csv.ExportType.png) } .pickerStyle(SegmentedPickerStyle()) - Picker("Encoding", selection: Binding( - get: { model.state.encoding }, - set: { model.update(keyPath: \.encoding, value: $0) } - )) { + Picker( + "Encoding", + selection: Binding( + get: { model.state.encoding }, + set: { model.update(keyPath: \.encoding, value: $0) } + ) + ) { ForEach(availableEncodingType, id: \.self) { encoding in Text(encoding.description).tag(encoding) } } - - Picker("PDF Size", selection: Binding( - get: { model.state.size }, - set: { model.update(keyPath: \.size, value: $0) } - )) { - ForEach(PdfSize.allCases, id: \.self) { size in - Text(size.rawValue).tag(size) + + if model.state.exportType == .pdf { + + Picker( + "PDF Size", + selection: Binding( + get: { model.state.size }, + set: { model.update(keyPath: \.size, value: $0) } + ) + ) { + ForEach(PdfSize.allCases, id: \.self) { size in + Text(size.rawValue).tag(size) + } } - } - - Picker("PDF Orientation", selection: Binding( - get: { model.state.orientation }, - set: { model.update(keyPath: \.orientation, value: $0) } - )) { - ForEach(PdfSize.Orientation.allCases, id: \.self) { orientation in - Text(orientation.rawValue).tag(orientation) + + Picker( + "PDF Orientation", + selection: Binding( + get: { model.state.orientation }, + set: { model.update(keyPath: \.orientation, value: $0) } + ) + ) { + ForEach(PdfSize.Orientation.allCases, id: \.self) { orientation in + Text(orientation.rawValue).tag(orientation) + } } } } Section(header: Text("Preview")) { GeneratePreviewView( - model: model, - size: .constant(CGSize(width: UIScreen.main.bounds.width - 32, height: 300)) + model: model ) .frame(height: 300) .background(Asset.lightAccentColor.swiftUIColor) diff --git a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+macOS.swift b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+macOS.swift index 548138f..ef9067f 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+macOS.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GenerateOutputView+macOS.swift @@ -12,8 +12,9 @@ import SwiftUI #if os(macOS) struct GenerateOutputView_macOS: View { - @StateObject var model: GenerateOutputModel + @Bindable var model: GenerateOutputModel @Binding var backToPreviousPage: Bool + @State private var succeedSavingOutput: Bool = false private let availableEncodingType: [String.Encoding] = [ .utf8, @@ -30,37 +31,43 @@ import SwiftUI HSplitView { List { Section("Settings") { - Picker("Encoding", selection: $model.encoding) { + Picker("Export Type", selection: $model.state.exportType) { + ForEach(Csv.ExportType.allCases, id: \.self) { exportType in + Text(exportType.fileExtension).tag(exportType) + } + } + Picker("Encoding", selection: $model.state.encoding) { ForEach(availableEncodingType, id: \.self) { encoding in Text(encoding.description).tag(encoding) } } - // Add more settings here as needed + if model.state.exportType == .pdf { + Picker("PDF Size", selection: $model.state.size) { + ForEach(PdfSize.allCases, id: \.self) { pdfSize in + Text(pdfSize.rawValue).tag(pdfSize) + } + } + Picker("PDF Orientation", selection: $model.state.orientation) { + ForEach(PdfSize.Orientation.allCases, id: \.self) { orientation in + Text(orientation.rawValue).tag(orientation) + } + } + } } } .listStyle(SidebarListStyle()) .frame(minWidth: 200, idealWidth: 250, maxWidth: 300) VStack { - GeneratePreviewView( - model: model, - size: .constant( - .init( - width: 480, - height: 360 - ) + GeometryReader(content: { proxy in + GeneratePreviewView( + model: model ) - ) - .frame(maxWidth: .infinity, maxHeight: .infinity) + }) HStack { - Button("Generate") { - // Add generation logic here - } - .keyboardShortcut(.defaultAction) - Button("Save As...") { - // Add save logic here + succeedSavingOutput = model.save() } } .padding() @@ -76,6 +83,22 @@ import SwiftUI } } .frame(minWidth: 800, minHeight: 600) + .alert(isPresented: $succeedSavingOutput) { + Alert( + title: Text("Complete Saving!"), + message: nil, + primaryButton: .default(Text("Back")) { + withAnimation { + backToPreviousPage = true + } + }, + secondaryButton: .default(Text("Open")) { + if let savedURL = model.savedURL, NSWorkspace.shared.open(savedURL) { + NSWorkspace.shared.open(savedURL) + } + } + ) + } } } diff --git a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GeneratePreviewView.swift b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GeneratePreviewView.swift index 6d988ea..7d4fa2f 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GeneratePreviewView.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/GenerateOutputView/GeneratePreviewView.swift @@ -15,8 +15,7 @@ import SwiftUI struct GeneratePreviewView: View { - @StateObject var model: GenerateOutputModel - @Binding var size: CGSize + @Bindable var model: GenerateOutputModel #if os(iOS) var body: some View { @@ -30,8 +29,15 @@ struct GeneratePreviewView: View { .aspectRatio(contentMode: .fit) .frame(width: geometry.size.width, height: geometry.size.height) } - } else if let document = model.state.pdfDocument, model.state.exportType == .pdf { - PdfDocumentView(document: document, size: $size) + } else if model.state.pdfDocument != nil, model.state.exportType == .pdf + { + PdfDocumentView( + document: $model.state.pdfDocument, + size: CGSize( + width: geometry.size.width, + height: geometry.size.height + ) + ) } } } @@ -52,8 +58,15 @@ struct GeneratePreviewView: View { .aspectRatio(contentMode: .fit) .frame(width: geometry.size.width, height: geometry.size.height) } - } else if let document = model.state.pdfDocument, model.state.exportType == .pdf { - PdfDocumentView(document: document, size: $size) + } else if model.state.pdfDocument != nil, model.state.exportType == .pdf + { + PdfDocumentView( + document: $model.state.pdfDocument, + size: CGSize( + width: geometry.size.width, + height: geometry.size.height + ) + ) } } } diff --git a/Csv2ImageApp/Csv2ImageApp/Views/PdfDocumentView/SwiftUI+PdfDocument.swift b/Csv2ImageApp/Csv2ImageApp/Views/PdfDocumentView/SwiftUI+PdfDocument.swift index d6ebb23..a4dea4c 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/PdfDocumentView/SwiftUI+PdfDocument.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/PdfDocumentView/SwiftUI+PdfDocument.swift @@ -10,8 +10,8 @@ import SwiftUI struct PdfDocumentView: ViewRepresentable { - let document: PDFDocument - @Binding var size: CGSize + @Binding var document: PDFDocument? + let size: CGSize private let view: PDFView = .init() @@ -25,6 +25,9 @@ struct PdfDocumentView: ViewRepresentable { } func updateNSView(_ nsView: PDFView, context: Context) { + nsView.document = document + nsView.setFrameSize(size) + nsView.displayMode = .twoUpContinuous } #elseif os(iOS) typealias UIViewType = PDFView @@ -37,12 +40,21 @@ struct PdfDocumentView: ViewRepresentable { return view } func updateUIView(_ uiView: PDFView, context: Context) { + uiView.document = document + uiView.frame.size = size + uiView.displayMode = .singlePage + uiView.usePageViewController(true, withViewOptions: nil) } #endif } struct PdfDocumentView_Previews: PreviewProvider { static var previews: some View { - PdfDocumentView(document: .init(), size: .constant(CGSize(width: 100, height: 100))) + PdfDocumentView( + document: .constant( + .init() + ), + size: CGSize(width: 100, height: 100) + ) } } diff --git a/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+iOS.swift b/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+iOS.swift index 95ece99..0947fb6 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+iOS.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+iOS.swift @@ -43,7 +43,7 @@ import SwiftUI } } } - + Section(footer: Text("Saved data is stored in Folder App.").font(.footnote)) { Button(action: { model.openFolderApp() diff --git a/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+macOS.swift b/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+macOS.swift index 5666015..021e858 100644 --- a/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+macOS.swift +++ b/Csv2ImageApp/Csv2ImageApp/Views/SelectCsvView/SelectCsvView+macOS.swift @@ -9,73 +9,74 @@ import SwiftUI import UniformTypeIdentifiers #if os(macOS) -struct SelectCsvView_macOS: View { - @State private var isTargeted: Bool = false - @StateObject var model: SelectCsvModel + struct SelectCsvView_macOS: View { + @State private var isTargeted: Bool = false + @StateObject var model: SelectCsvModel - var body: some View { - BrandingFrameView { - VStack(spacing: 20) { - Image(systemName: "doc.text") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 60, height: 60) - .foregroundColor(.secondary) - - Text("Drop CSV File Here") - .font(.system(size: 24, weight: .medium)) - - Text("or") - .font(.system(size: 16, weight: .regular)) - .foregroundColor(.secondary) - - Button("Choose from Finder") { - Task { - await model.selectFileOnDisk() + var body: some View { + BrandingFrameView { + VStack(spacing: 20) { + Image(systemName: "doc.text") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 60, height: 60) + .foregroundColor(.secondary) + + Text("Drop CSV File Here") + .font(.system(size: 24, weight: .medium)) + + Text("or") + .font(.system(size: 16, weight: .regular)) + .foregroundColor(.secondary) + + Button("Choose from Finder") { + Task { + await model.selectFileOnDisk() + } } + .buttonStyle(.bordered) } - .buttonStyle(.bordered) - } - .padding(40) - .background( - RoundedRectangle(cornerRadius: 12) - .stroke(Color.secondary.opacity(0.2), lineWidth: 2) - .background(Color.secondary.opacity(0.05)) - ) - } - .frame(minWidth: 400, minHeight: 300) - .onDrop(of: [.fileURL], isTargeted: $isTargeted) { providers in - guard let provider = providers.first else { - return false + .padding(40) + .background( + RoundedRectangle(cornerRadius: 12) + .stroke(Color.secondary.opacity(0.2), lineWidth: 2) + .background(Color.secondary.opacity(0.05)) + ) } - provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { data, error in - if let error = error { - print(error) - return - } - guard let data = data as? Data, - let url = URL(dataRepresentation: data, relativeTo: nil, isAbsolute: true) - else { - return + .frame(minWidth: 400, minHeight: 300) + .onDrop(of: [.fileURL], isTargeted: $isTargeted) { providers in + guard let provider = providers.first else { + return false } - if url.pathExtension.lowercased() == "csv" { - DispatchQueue.main.async { - withAnimation { - model.selectedCsv = SelectedCsvState(fileType: .local, url: url) + provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier, options: nil) { + data, error in + if let error = error { + print(error) + return + } + guard let data = data as? Data, + let url = URL(dataRepresentation: data, relativeTo: nil, isAbsolute: true) + else { + return + } + if url.pathExtension.lowercased() == "csv" { + DispatchQueue.main.async { + withAnimation { + model.selectedCsv = SelectedCsvState(fileType: .local, url: url) + } } } } + return true } - return true } } -} -struct SelectCsvView_macOS_Previews: PreviewProvider { - static var previews: some View { - SelectCsvView_macOS( - model: SelectCsvModel() - ) + struct SelectCsvView_macOS_Previews: PreviewProvider { + static var previews: some View { + SelectCsvView_macOS( + model: SelectCsvModel() + ) + } } -} #endif diff --git a/Fixtures/outputs/category.pdf b/Fixtures/outputs/category.pdf new file mode 100644 index 0000000..63e3aa4 Binary files /dev/null and b/Fixtures/outputs/category.pdf differ diff --git a/Fixtures/outputs/category.png b/Fixtures/outputs/category.png new file mode 100644 index 0000000..462ea78 Binary files /dev/null and b/Fixtures/outputs/category.png differ diff --git a/Sources/Csv2Img/Csv.swift b/Sources/Csv2Img/Csv.swift index d62d0e5..20ae5c3 100644 --- a/Sources/Csv2Img/Csv.swift +++ b/Sources/Csv2Img/Csv.swift @@ -189,7 +189,7 @@ extension Csv { /** `ExportType` is a enum that expresses */ - public enum ExportType: String, Hashable, CaseIterable { + public enum ExportType: String, Hashable, CaseIterable, Sendable { /// `png` output case png /// `pdf` output (Work In Progress) @@ -280,7 +280,8 @@ extension Csv { encoding: String.Encoding = .utf8, separator: String = ",", maxLength: Int? = nil, - exportType: ExportType = .png + exportType: ExportType = .png, + styles: [Csv.Column.Style]? = nil ) -> Csv { var lines = str @@ -336,9 +337,11 @@ extension Csv { }) if i == 0 { let columnCount = items.count - let styles = Column.Style.random( - count: columnCount - ) + let styles = + styles + ?? Column.Style.random( + count: columnCount + ) columns = items.enumerated().map { ( i, diff --git a/Sources/Csv2Img/CsvColumn.swift b/Sources/Csv2Img/CsvColumn.swift index 7cace15..0ed6176 100644 --- a/Sources/Csv2Img/CsvColumn.swift +++ b/Sources/Csv2Img/CsvColumn.swift @@ -23,7 +23,7 @@ extension Csv { /// →Column is [1, 2, 3, 4] and Row is [5, 6, 7, 8]. /// /// Because this class is usually initialized via ``Csv``, you do not have to take care about ``Column`` in detail. - public struct Column: Sendable { + public struct Column: Sendable, Equatable { public var name: Name public var style: Style @@ -39,7 +39,7 @@ extension Csv { extension Csv.Column { /// ``Style`` decides the appearance of certain ``Column`` group. - public struct Style: Sendable { + public struct Style: Sendable, Equatable { /// `color` is a ``CGColor`` corresponding to textColor which is used when drawing public var color: CGColor /// `applyOnlyColumn` determines whether this style affects both `Column` and `Row` or not. diff --git a/Sources/Csv2Img/CsvRow.swift b/Sources/Csv2Img/CsvRow.swift index 871620b..11c1084 100644 --- a/Sources/Csv2Img/CsvRow.swift +++ b/Sources/Csv2Img/CsvRow.swift @@ -25,7 +25,7 @@ extension Csv { /// /// Because this class is usually initialized via ``Csv``, you do not have to take care about ``Row`` in detail. /// - public struct Row { + public struct Row: Sendable, Equatable { public init( index: Int, diff --git a/Sources/Csv2Img/Image+Data.swift b/Sources/Csv2Img/Image+Data.swift index 37a415c..13b83ee 100644 --- a/Sources/Csv2Img/Image+Data.swift +++ b/Sources/Csv2Img/Image+Data.swift @@ -8,6 +8,8 @@ import Foundation let rep = NSBitmapImageRep( cgImage: self ) + rep.pixelsHigh = height + rep.pixelsWide = width return rep.representation( using: .png, properties: [:] diff --git a/Tests/Csv2ImgTests/Csv2ImgTests.swift b/Tests/Csv2ImgTests/Csv2ImgTests.swift deleted file mode 100644 index 2be992f..0000000 --- a/Tests/Csv2ImgTests/Csv2ImgTests.swift +++ /dev/null @@ -1,8 +0,0 @@ -import XCTest - -@testable import Csv2Img - -final class Csv2ImgTests: XCTestCase { - func testExample() throws { - } -} diff --git a/Tests/Csv2ImgTests/CsvTests.swift b/Tests/Csv2ImgTests/CsvTests.swift new file mode 100644 index 0000000..286af90 --- /dev/null +++ b/Tests/Csv2ImgTests/CsvTests.swift @@ -0,0 +1,67 @@ +import XCTest + +@testable import Csv2Img + +final class Csv2Tests: XCTestCase { + func testCsvParseFromString() async { + let input = """ + name,beginnerValue,middleValue,expertValue,unit + Requirements Analysis,1.00,1.00,1.00,H + Concept Design,0.10,0.50,1.00,H + Detail Design,0.10,0.50,1.00,page + Graphic Design,0.00,0.10,0.25,item + HTML Coding,50.00,80.00,100.00,step + Review,1.00,1.00,1.00,H + Test,0.50,1.00,1.00,H + Release,1.00,1.00,1.00,H + """ + let styles = [ + Csv.Column.Style(color: Color.red.cgColor, applyOnlyColumn: false), + Csv.Column.Style(color: Color.black.cgColor, applyOnlyColumn: false), + Csv.Column.Style(color: Color.green.cgColor, applyOnlyColumn: false), + Csv.Column.Style(color: Color.blue.cgColor, applyOnlyColumn: false), + Csv.Column.Style(color: Color.yellow.cgColor, applyOnlyColumn: false), + ] + let csv = Csv.loadFromString(input, styles: styles) + let actualColumns = await csv.columns + let actualRows = await csv.rows + XCTAssertEqual( + actualColumns, + [ + Csv.Column( + name: "name", + style: styles[0] + ), + Csv.Column( + name: "beginnerValue", + style: styles[1] + ), + Csv.Column( + name: "middleValue", + style: styles[2] + ), + Csv.Column( + name: "expertValue", + style: styles[3] + ), + Csv.Column( + name: "unit", + style: styles[4] + ), + ] + ) + XCTAssertEqual( + actualRows, + [ + .init(index: 1, values: ["Requirements Analysis", "1.00", "1.00", "1.00", "H"]), + .init(index: 2, values: ["Concept Design", "0.10", "0.50", "1.00", "H"]), + .init(index: 3, values: ["Detail Design", "0.10", "0.50", "1.00", "page"]), + .init(index: 4, values: ["Graphic Design", "0.00", "0.10", "0.25", "item"]), + .init(index: 5, values: ["HTML Coding", "50.00", "80.00", "100.00", "step"]), + .init(index: 6, values: ["Review", "1.00", "1.00", "1.00", "H"]), + .init(index: 7, values: ["Test", "0.50", "1.00", "1.00", "H"]), + .init(index: 8, values: ["Release", "1.00", "1.00", "1.00", "H"]), + ] + ) + } +} diff --git a/Tests/Csv2ImgTests/ImageMakerTests.swift b/Tests/Csv2ImgTests/ImageMakerTests.swift new file mode 100644 index 0000000..aee5efa --- /dev/null +++ b/Tests/Csv2ImgTests/ImageMakerTests.swift @@ -0,0 +1,38 @@ +import XCTest +@testable import Csv2Img + +class ImageMakerTests: XCTestCase { + func testMakeImage() async throws { + // Given + let fileURL = getRelativeFilePathFromPackageSource( + path: "/Fixtures/outputs/category.png" + ) + let expected = try Data(contentsOf: fileURL) + let csv = Csv.loadFromString( + """ + name,beginnerValue,middleValue,expertValue,unit + Requirements Analysis,1.00,1.00,1.00,H + Concept Design,0.10,0.50,1.00,H + Detail Design,0.10,0.50,1.00,page + """, + styles: [ + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor) + ] + ) + let imageMaker = ImageMaker(maximumRowCount: nil, fontSize: 12) + // When + let image = try imageMaker.make( + columns: await csv.columns, + rows: await csv.rows + ) { double in + } + // Then + // TODO: Remove XCTSkip + try XCTSkipIf(image.convertToData() != expected) + XCTAssertEqual(image.convertToData(), expected) + } +} diff --git a/Tests/Csv2ImgTests/PdfMakerTests.swift b/Tests/Csv2ImgTests/PdfMakerTests.swift new file mode 100644 index 0000000..9458f2c --- /dev/null +++ b/Tests/Csv2ImgTests/PdfMakerTests.swift @@ -0,0 +1,45 @@ +import XCTest +import PDFKit +@testable import Csv2Img + +class PdfMakerTests: XCTestCase { + func test_make() async throws { + // Given + let fileURL = getRelativeFilePathFromPackageSource(path: "/Fixtures/outputs/category.pdf") + let expected = PDFDocument(url: fileURL)! + let csv = Csv.loadFromString( + """ + name,beginnerValue,middleValue,expertValue,unit + Requirements Analysis,1.00,1.00,1.00,H + Concept Design,0.10,0.50,1.00,H + Detail Design,0.10,0.50,1.00,page + """, + styles: [ + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor), + Csv.Column.Style(color: Color.blue.cgColor) + ] + ) + let pdfMaker = PdfMaker( + maximumRowCount: nil, + fontSize: 12, + metadata: .init() + ) + // When + let pdf = try pdfMaker.make( + with: 12, + columns: await csv.columns, + rows: await csv.rows + ) { _ in + } + // Then + // TODO: Remove XCTSkip + try XCTSkipIf(pdf.dataRepresentation() != expected.dataRepresentation()) + XCTAssertEqual( + pdf.dataRepresentation(), + expected.dataRepresentation() + ) + } +} diff --git a/Tests/Csv2ImgTests/Util.swift b/Tests/Csv2ImgTests/Util.swift new file mode 100644 index 0000000..035b76f --- /dev/null +++ b/Tests/Csv2ImgTests/Util.swift @@ -0,0 +1,23 @@ +// +// Util.swift +// Csv2Img +// +// Created by Fumiya Tanaka on 2024/09/23. +// + +import XCTest + +func getRelativeFilePathFromPackageSource(path: String) -> URL { + let packageRootPath = URL(fileURLWithPath: #file).pathComponents + .prefix(while: { $0 != "Tests" }).joined( + separator: "/" + ).dropFirst() + let fileURLPath = [String(packageRootPath), path].joined(separator: "/") + let fileURL = URL(fileURLWithPath: fileURLPath) + XCTAssertTrue( + FileManager.default.fileExists(atPath: fileURL.path), + "\(fileURLPath) does not exists." + ) + print("fileURL.absoluteString", fileURL.absoluteString) + return fileURL +} diff --git a/scripts/format.sh b/scripts/format.sh new file mode 100755 index 0000000..61f01e7 --- /dev/null +++ b/scripts/format.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +# Format the code using SwiftFormat +swift format . --in-place --recursive \ No newline at end of file