From 984eea68350b7847596392e19b408111581c98ed Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Thu, 8 Oct 2026 12:32:59 +0900 Subject: [PATCH] =?UTF-8?q?test(address):=20=EC=A3=BC=EC=86=8C=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=EA=B3=84=EC=95=BD=EC=9D=84=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- work/tests/KakaoPostcodeResultCheck.swift | 25 ++++++++ work/tests/check-gift-address-search.sh | 74 +++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 work/tests/KakaoPostcodeResultCheck.swift create mode 100644 work/tests/check-gift-address-search.sh diff --git a/work/tests/KakaoPostcodeResultCheck.swift b/work/tests/KakaoPostcodeResultCheck.swift new file mode 100644 index 00000000..c556dc0f --- /dev/null +++ b/work/tests/KakaoPostcodeResultCheck.swift @@ -0,0 +1,25 @@ +import Foundation + +@main +enum KakaoPostcodeResultCheck { + static func main() { + let result = KakaoPostcodeResult(payload: [ + "zonecode": "01234", "address": "서울 지번 주소", "roadAddress": "다른 도로명 주소" + ]) + precondition(result?.zonecode == "01234", "preserve leading zero") + precondition(result?.address == "서울 지번 주소", "use exact address, not roadAddress") + let exact = KakaoPostcodeResult(payload: ["zonecode": " 01234 ", "address": " 주소 "]) + precondition(exact?.zonecode == " 01234 " && exact?.address == " 주소 ", "do not rewrite values") + let invalid: [Any] = [ + "not a dictionary", [:], ["address": "주소"], ["zonecode": "01234"], + ["zonecode": 1234, "address": "주소"], ["zonecode": "01234", "address": 1], + ["zonecode": "", "address": "주소"], ["zonecode": " \n", "address": "주소"], + ["zonecode": "01234", "address": ""], ["zonecode": "01234", "address": "\t\n"], + ["zonecode": NSNull(), "address": "주소"], ["zonecode": "01234", "address": NSNull()] + ] + for payload in invalid { + precondition(KakaoPostcodeResult(payload: payload) == nil, "reject malformed result") + } + print("PASS KakaoPostcodeResult: 15 assertions") + } +} diff --git a/work/tests/check-gift-address-search.sh b/work/tests/check-gift-address-search.sh new file mode 100644 index 00000000..15563441 --- /dev/null +++ b/work/tests/check-gift-address-search.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +TEMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TEMP_DIR"' EXIT +ruby - "$ROOT" "$TEMP_DIR" "${1:-check}" <<'RUBY' +require 'digest' +root, temp, mode = ARGV +def require_check(condition, message) + abort "FAIL gift-address-search: #{message}" unless condition +end +send_view = File.read("#{root}/SodaLive/Sources/V2/Gift/Send/GiftSendView.swift") +size = send_view[/ private var sizeSection:.*?(?= private var categorySection:)/m] +title = send_view[/ private func sizeTitle.*?(?=\n}\s*\z)/m] +if mode == 'baseline' + puts "sizeSection: #{Digest::SHA256.hexdigest(size)}" + puts "sizeTitle: #{Digest::SHA256.hexdigest(title)}" + exit +end +require_check(Digest::SHA256.hexdigest(size) == 'bc542be1ff84f1f8818b83d3b2ff04cd58799d3b2719f57592b729d69fe92ecd', 'sizeSection unchanged from pre-edit workspace') +require_check(Digest::SHA256.hexdigest(title) == '0a341a8584ae2e673d9fd1fd5f8bbeccc970ec90a65ff002614e113ffdba9874', 'sizeTitle unchanged from pre-edit workspace') +puts 'PASS gift-address-search: sizeSection and sizeTitle preserved' +path = "#{root}/SodaLive/Sources/V2/Component/Address/KakaoPostcodeSearchView.swift" +require_check(File.exist?(path), 'missing KakaoPostcodeSearchView.swift') +web = File.read(path) +shared = File.read("#{root}/SodaLive/Sources/V2/Component/Address/SodaAddressInputView.swift") +require_check(shared.match?(/KakaoPostcodeSearchView\s*\{\s*result in\s*if zipCode != result\.zonecode \|\| address != result\.address\s*\{\s*addressDetail = ""\s*\}\s*zipCode = result\.zonecode\s*address = result\.address\s*isSearching = false\s*\}/m), 'clear detail only when old postcode/base pair differs, before assigning selection') +['우편번호', '주소', '상세주소'].each do |label| + require_check(shared.include?("Text(\"#{label}\")"), "visible address label #{label}") +end +require_check(shared.include?('Text("주소 검색")') && shared.include?('.accessibilityLabel("주소 검색")'), 'address search visible and accessibility copy') +[ + 'https://t1.kakaocdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js', + 'loadHTMLString', 'https://postcode.map.kakao.com/', 'new kakao.Postcode', '.embed(', + 'zonecode: data.zonecode, address: data.address', 'WKScriptMessageHandler', + 'add(context.coordinator, name:', 'removeScriptMessageHandler(forName:', + 'message.name ==', 'KakaoPostcodeResult(payload: message.body)', + 'didFail navigation:', 'didFailProvisionalNavigation', 'webViewWebContentProcessDidTerminate', + 'navigationDelegate = nil', 'uiDelegate = nil', 'ProgressView', 'Button("닫기")', + 'viewport', 'height:100%', 'onerror=' +].each { |probe| require_check(web.include?(probe), "web contract #{probe}") } +require_check(!web.include?('data.roadAddress'), 'do not replace selected address') +require_check(shared.scan(/TextField\(/).length == 1 && shared.include?('text: $addressDetail'), 'only detail editable') +['Text(value.isEmpty ? placeholder : value)', + 'readonlyField(zipCode, placeholder: "우편번호를 입력해 주세요", label: "우편번호")', + 'readonlyField(address, placeholder: "주소를 입력해 주세요", label: "기본 주소")', + '.sheet(isPresented:', 'zipCode = result.zonecode', 'address = result.address', 'isSearching = false', + 'Text("주소")', 'SodaSpacing.s4', 'SodaSpacing.s8', 'SodaSpacing.s14', 'SodaRadius.r14', + '.appFont(.body5)', '.appFont(.body3)', '.frame(minHeight: 44)', 'Capsule()', + '.accessibilityLabel("주소 검색")', '.accessibilityLabel("상세 주소")' +].each { |probe| require_check(shared.include?(probe), "shared contract #{probe}") } +{ + 'Send/Components/GiftSenderFormView.swift' => ['$zipCode', '$address', '$addressDetail', '택배 발송 시 이름과 동일하게 적어주세요', '발송 확인 외의 용도로 사용하지 않으며, 전달이 완료되면 파기됩니다'], + 'RecipientAddress/GiftRecipientAddressFormView.swift' => ['$viewModel.recipientZipCode', '$viewModel.recipientAddress', '$viewModel.recipientAddressDetail', '입력한 이름으로 선물이 전달돼요', '제출 후 수정할 수 없으니 입력한 정보를 다시 한번 확인해 주세요'] +}.each do |file, probes| + source = File.read("#{root}/SodaLive/Sources/V2/Gift/#{file}") + (probes + ['SodaAddressInputView(', 'SodaSpacing.s20', 'SodaSpacing.s4', 'SodaSpacing.s14', 'SodaRadius.r14', '.appFont(.body3)', '문제 발생 시 연락이 가능한 번호를 입력해 주세요']).each do |probe| + require_check(source.include?(probe), "#{file}: #{probe}") + end +end +category = send_view[/ private var categorySection:.*?(?= private var agreementsSection:)/m] +require_check(!category.include?('minHeight: 77'), 'compact category height') +['.appFont(.body3)', '.padding(SodaSpacing.s14)', '.cornerRadius(SodaRadius.r14)', 'Menu {', 'viewModel.selectCategory(category)', '.disabled(viewModel.categories.isEmpty)', 'requiresDamageWaiver'].each do |probe| + require_check(category.include?(probe), "category contract #{probe}") +end +require_check(web.lines.count < 250 && shared.lines.count < 250, 'shared files below 250 lines') +model = web.split('struct KakaoPostcodeSearchView: View').first.lines.reject { |line| line =~ /^import (SwiftUI|WebKit)/ }.join +File.write("#{temp}/KakaoPostcodeResult.swift", model) +puts 'PASS gift-address-search: bridge, lifecycle, readonly shared forms and compact category source contracts' +RUBY +if [[ "${1:-check}" == "baseline" ]]; then exit 0; fi +swiftc -module-cache-path "$TEMP_DIR/module-cache" "$TEMP_DIR/KakaoPostcodeResult.swift" \ + "$ROOT/work/tests/KakaoPostcodeResultCheck.swift" -o "$TEMP_DIR/check" +"$TEMP_DIR/check"