mirror of
https://github.com/Aminark/legado-ios.git
synced 2026-10-24 21:01:22 +08:00
支持 EPUB 2/3 导入、目录、封面和章节插图,保留本地书管理与阅读进度。 修复 JSON 模板、动态替换与请求参数兼容,以及 RSS 翻页和连续滚动定位。 更新编辑器工具栏和回归测试,发布版本为 0.1.12(build 18)。
90 lines
4.7 KiB
Swift
90 lines
4.7 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import LegadoCore
|
|
|
|
// The payloads and host below are synthetic and never use network access.
|
|
struct SampledRequestRegressionTests {
|
|
@Test func equalsSeparatedSourceHeaderReachesSearchTransport() async throws {
|
|
var source = source()
|
|
source.header = #"{"type"="app"}"#
|
|
source.searchUrl = #"/search,{"method":"POST","body":"keyword={{key}}&page={{page-1}}"}"#
|
|
let transport = SampledRequestTransport()
|
|
let result = await BookSearchService(transport: transport).search(keyword: "斗罗大陆", page: 1, source: source)
|
|
#expect(result.diagnostics.isEmpty)
|
|
#expect(result.books.count == 1)
|
|
let request = try #require(await transport.requests().first)
|
|
#expect(request.value(forHTTPHeaderField: "type") == "app")
|
|
#expect(request.httpBody.map { String(decoding: $0, as: UTF8.self) } == "keyword=%E6%96%97%E7%BD%97%E5%A4%A7%E9%99%86&page=0")
|
|
}
|
|
|
|
@Test func stringEncodedLenientHeadersReachSearchTransport() async throws {
|
|
var source = source()
|
|
source.searchUrl = #"/search?word={{key}},{"headers":"{os:'pc'}"}"#
|
|
let transport = SampledRequestTransport()
|
|
let result = await BookSearchService(transport: transport).search(keyword: "斗罗大陆", page: 1, source: source)
|
|
#expect(result.diagnostics.isEmpty)
|
|
#expect(result.books.count == 1)
|
|
let request = try #require(await transport.requests().first)
|
|
#expect(request.value(forHTTPHeaderField: "os") == "pc")
|
|
}
|
|
|
|
@Test func unquotedKeywordInObjectBodyPreservesJSONValueTypes() async throws {
|
|
var source = source()
|
|
source.searchUrl = #"/search,{"method":"POST","body":{"keyword":{{key}},"page_index":{{page}},"page_size":20,"enabled":true,"cursor":null}}"#
|
|
let transport = SampledRequestTransport()
|
|
let result = await BookSearchService(transport: transport).search(keyword: "斗罗大陆", page: 1, source: source)
|
|
#expect(result.diagnostics.isEmpty)
|
|
#expect(result.books.count == 1)
|
|
let request = try #require(await transport.requests().first)
|
|
let body = try JSONValue.decode(data: #require(request.httpBody))
|
|
#expect(body["keyword"] == .string("斗罗大陆"))
|
|
#expect(body["page_index"] == .number(1))
|
|
#expect(body["page_size"] == .number(20))
|
|
#expect(body["enabled"] == .bool(true))
|
|
#expect(body["cursor"] == .null)
|
|
#expect(request.value(forHTTPHeaderField: "Content-Type")?.hasPrefix("application/json") == true)
|
|
}
|
|
|
|
@Test func lenientSeparatorsDoNotRewriteQuotedCookieOrURLValues() throws {
|
|
let value = try LegadoURLBuilder.decodeLenientJSON(#"{type=>app, 'Cookie':'token=a=b; mode=pc', referer:'https://fixture.invalid/?a=b', retry:2, enabled:false, empty:null}"#)
|
|
#expect(value["type"] == .string("app"))
|
|
#expect(value["Cookie"] == .string("token=a=b; mode=pc"))
|
|
#expect(value["referer"] == .string("https://fixture.invalid/?a=b"))
|
|
#expect(value["retry"] == .number(2))
|
|
#expect(value["enabled"] == .bool(false))
|
|
#expect(value["empty"] == .null)
|
|
}
|
|
|
|
@Test func unquotedWebViewFlagUsedByAudioSourceParses() throws {
|
|
let request = try LegadoURLBuilder().prepare("https://request.fixture.invalid/audio,{webView:“true”}")
|
|
#expect(request.useWebView)
|
|
}
|
|
|
|
@Test func lenientValuesKeepCommentsAndPutRuleExpressionsWorking() throws {
|
|
let options = try LegadoURLBuilder.decodeLenientJSON("{retry: 2 /* comment */, enabled: FALSE, word: 小说}")
|
|
#expect(options["retry"] == .number(2))
|
|
#expect(options["enabled"] == .bool(false))
|
|
#expect(options["word"] == .string("小说"))
|
|
let put = try LegadoURLBuilder.decodeLenientJSON("{id:$.books[0].id, name:name}", quoteUnquotedValues: true)
|
|
#expect(put["id"] == .string("$.books[0].id"))
|
|
#expect(put["name"] == .string("name"))
|
|
#expect(throws: (any Error).self) { try LegadoURLBuilder.decodeLenientJSON("{broken:}") }
|
|
}
|
|
|
|
private func source() -> BookSource {
|
|
var source = BookSource(bookSourceUrl: "https://request.fixture.invalid", bookSourceName: "请求兼容测试")
|
|
source.ruleSearch = .init(bookList: "$.books", name: "$.name", bookUrl: "$.url")
|
|
return source
|
|
}
|
|
}
|
|
|
|
private actor SampledRequestTransport: HTTPTransport {
|
|
private var values: [URLRequest] = []
|
|
func data(for request: URLRequest) async throws -> HTTPResponse {
|
|
values.append(request)
|
|
return .init(data: Data(#"{"books":[{"name":"测试书","url":"/book/42"}]}"#.utf8),
|
|
statusCode: 200, headers: [:], finalURL: try #require(request.url))
|
|
}
|
|
func requests() -> [URLRequest] { values }
|
|
}
|