From 08e67ce3084106dc72ee7caf50719798a1b95aab Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Tue, 10 Feb 2026 16:26:23 -0500 Subject: [PATCH] feat: Adds privacy policy. --- Public/css/output.css | 6 + README.md | 2 +- .../App/Middleware/ViewRoute+middleware.swift | 2 +- Sources/ManualDCore/Routes/ViewRoute.swift | 5 + Sources/ViewController/Live.swift | 4 + .../ViewController/Views/PrivacyPolicy.swift | 699 ++++++++++++++++++ .../ViewController/Views/User/LoginForm.swift | 12 +- TODO.md | 3 + .../ViewControllerTests.swift | 13 +- .../ViewControllerTests/login.1.html | 5 +- .../ViewControllerTests/privacyPolicy.1.html | 475 ++++++++++++ .../ViewControllerTests/signup.1.html | 5 +- 12 files changed, 1225 insertions(+), 6 deletions(-) create mode 100644 Sources/ViewController/Views/PrivacyPolicy.swift create mode 100644 Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/privacyPolicy.1.html diff --git a/Public/css/output.css b/Public/css/output.css index b5abb82..e668703 100644 --- a/Public/css/output.css +++ b/Public/css/output.css @@ -5303,6 +5303,9 @@ .-mx-2 { margin-inline: calc(var(--spacing) * -2); } + .mx-6 { + margin-inline: calc(var(--spacing) * 6); + } .mx-10 { margin-inline: calc(var(--spacing) * 10); } @@ -7949,6 +7952,9 @@ .p-6 { padding: calc(var(--spacing) * 6); } + .p-10 { + padding: calc(var(--spacing) * 10); + } .menu-title { @layer daisyui.l1.l2.l3 { padding-inline: calc(0.25rem * 3); diff --git a/README.md b/README.md index fb9db49..477c7f8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# swift-manual-d +# swift-duct-calc diff --git a/Sources/App/Middleware/ViewRoute+middleware.swift b/Sources/App/Middleware/ViewRoute+middleware.swift index 971c1f2..66a51b6 100644 --- a/Sources/App/Middleware/ViewRoute+middleware.swift +++ b/Sources/App/Middleware/ViewRoute+middleware.swift @@ -14,7 +14,7 @@ private let viewRouteMiddleware: [any Middleware] = [ extension SiteRoute.View { var middleware: [any Middleware]? { switch self { - case .home, .login, .signup, .test, .ductulator: + case .home, .login, .signup, .test, .ductulator, .privacyPolicy: return nil case .project, .user: return viewRouteMiddleware diff --git a/Sources/ManualDCore/Routes/ViewRoute.swift b/Sources/ManualDCore/Routes/ViewRoute.swift index 8d21790..aa8db4e 100644 --- a/Sources/ManualDCore/Routes/ViewRoute.swift +++ b/Sources/ManualDCore/Routes/ViewRoute.swift @@ -9,6 +9,7 @@ extension SiteRoute { /// The routes return html. public enum View: Equatable, Sendable { case home + case privacyPolicy case login(LoginRoute) case signup(SignupRoute) case project(ProjectRoute) @@ -25,6 +26,10 @@ extension SiteRoute { Route(.case(Self.home)) { Method.get } + Route(.case(Self.privacyPolicy)) { + Path { "privacy-policy" } + Method.get + } Route(.case(Self.login)) { SiteRoute.View.LoginRoute.router } diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index b87bd46..2e7c77b 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -22,6 +22,10 @@ extension ViewController.Request { return await view { HomeView() } + case .privacyPolicy: + return await view { + PrivacyPolicyView() + } case .test: // let projectID = UUID(uuidString: "E796C96C-F527-4753-A00A-EBCF25630663")! // return await view { diff --git a/Sources/ViewController/Views/PrivacyPolicy.swift b/Sources/ViewController/Views/PrivacyPolicy.swift new file mode 100644 index 0000000..a988125 --- /dev/null +++ b/Sources/ViewController/Views/PrivacyPolicy.swift @@ -0,0 +1,699 @@ +import Elementary + +struct PrivacyPolicyView: HTML, Sendable { + + var body: some HTML { + div(.class("p-10 space-y-4")) { + h1(.class("text-3xl font-bold")) { "Privacy Policy" } + p { + "Last updated: February 10, 2026" + } + p { + """ + This Privacy Policy describes Our policies and procedures on the collection, use and disclosure + of Your information when You use the Service and tells You about Your privacy rights and how the + law protects You. + """ + } + p { + """ + We use Your Personal Data to provide and improve the Service. By using the Service, You agree + to the collection and use of information in accordance with this Privacy Policy. This Privacy + Policy has been created with the help of the + """ + a( + .href("https://www.termsfeed.com/privacy-policy-generator/"), + .target(.blank) + ) { + " TermsFeed Privacy Policy Generator" + } + } + + h2(.class("text-2xl font-bold")) { "Interpretation and Definitions" } + h3(.class("text-xl font-bold")) { "Interpretation" } + + p { + """ + The words whose initial letters are capitalized have meanings defined under the + following conditions. The following definitions shall have the same meaning + regardless of whether they appear in singular or in plural. + """ + } + + h3(.class("text-xl font-bold")) { "Definitions" } + + p { "For the purposes of this Privacy Policy:" } + + ul(.class("list-disc px-6 space-y-2")) { + li { + span(.class("font-bold")) { + "Account" + } + " means a unique account created for You to access our Service or parts of our Service." + } + li { + span(.class("font-bold")) { + "Affiliate" + } + """ + means an entity that controls, is controlled by, or is under common control with a + party, where "control" means ownership of 50% or more of the shares, equity interest or other + securities entitled to vote for election of directors or other managing authority. + """ + } + li { + span(.class("font-bold")) { + "Company" + } + """ + (referred to as either "the Company", "We", "Us" or "Our" in this Privacy Policy) + refers to Duct Calc. + """ + } + li { + span(.class("font-bold")) { + "Cookies" + } + """ + are small files that are placed on Your computer, mobile device or any other device by + a website, containing the details of Your browsing history on that website among its many uses. + """ + } + li { + span(.class("font-bold")) { + "Country" + } + """ + refers to: Ohio, United States + """ + } + li { + span(.class("font-bold")) { + "Device" + } + """ + means any device that can access the Service such as a computer, a cell phone or a + digital tablet. + """ + } + li { + span(.class("font-bold")) { + "Personal Data" + } + """ + (or "Personal Information") is any information that relates to an identified or + identifiable individual. + + We use "Personal Data" and "Personal Information" interchangeably unless a law uses a specific + term. + """ + } + li { + span(.class("font-bold")) { + "Service" + } + """ + refers to the Website. + """ + } + li { + span(.class("font-bold")) { + "Service Provider" + } + """ + means any natural or legal person who processes the data on behalf of the + Company. It refers to third-party companies or individuals employed by the Company to facilitate + the Service, to provide the Service on behalf of the Company, to perform services related to the + Service or to assist the Company in analyzing how the Service is used. + """ + } + li { + span(.class("font-bold")) { + "Usage Data" + } + """ + refers to data collected automatically, either generated by the use of the Service + or from the Service infrastructure itself (for example, the duration of a page visit). + """ + } + li { + span(.class("font-bold")) { + "Website" + } + """ + refers to Duct Calc, accessible from [https://ductcalc.pro](https://ductcalc.pro). + """ + } + li { + span(.class("font-bold")) { + "You" + } + """ + means the individual accessing or using the Service, or the company, or other legal entity + on behalf of which such individual is accessing or using the Service, as applicable. + """ + } + } + + h2(.class("text-2xl font-bold")) { "Collecting and Using Your Personal Data" } + h3(.class("text-xl font-bold")) { "Types of Data Collected" } + h4(.class("text-lg font-bold")) { "Personal Data" } + + p { + """ + While using Our Service, We may ask You to provide Us with certain personally identifiable + information that can be used to contact or identify You. Personally identifiable information may + include, but is not limited to: + """ + } + + ul(.class("list-disc mx-6 space-y-2")) { + li { "Email address" } + li { "First and last name" } + li { "Phone number" } + li { "Address, State, Province, ZIP/Postal Code, City" } + } + + h4(.class("text-lg font-bold")) { "Usage Data" } + p { + """ + Usage Data may include information such as Your Device's Internet Protocol address (e.g. IP + address), browser type, browser version, the pages of our Service that You visit, the time and date + of Your visit, the time spent on those pages, unique device identifiers and other diagnostic data. + + When You access the Service by or through a mobile device, We may collect certain information + automatically, including, but not limited to, the type of mobile device You use, Your mobile + device's unique ID, the IP address of Your mobile device, Your mobile operating system, the type of + mobile Internet browser You use, unique device identifiers and other diagnostic data. + + We may also collect information that Your browser sends whenever You visit Our Service or when You + access the Service by or through a mobile device. + """ + } + + h4(.class("text-lg font-bold")) { "Tracking Technologies and Cookies" } + p { + """ + We use Cookies and similar tracking technologies to track the activity on Our Service and store + certain information. Tracking technologies We use include beacons, tags, and scripts to collect and + track information and to improve and analyze Our Service. The technologies We use may include: + """ + } + ul(.class("list-disc mx-6 space-y-2")) { + li { + span(.class("font-bold")) { "Cookies or Browser Cookies." } + """ + A cookie is a small file placed on Your Device. You can instruct + Your browser to refuse all Cookies or to indicate when a Cookie is being sent. However, if You do + not accept Cookies, You may not be able to use some parts of our Service. + """ + } + li { + span(.class("font-bold")) { "Web Beacons" } + """ + Certain sections of our Service and our emails may contain small electronic files + known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that + permit the Company, for example, to count users who have visited those pages or opened an email + and for other related website statistics (for example, recording the popularity of a certain + section and verifying system and server integrity). + """ + } + } + + p { + """ + + Cookies can be "Persistent" or "Session" Cookies. Persistent Cookies remain on Your personal + computer or mobile device when You go offline, while Session Cookies are deleted as soon as You + close Your web browser. + + Where required by law, we use non-essential cookies (such as analytics, advertising, and remarketing + cookies) only with Your consent. You can withdraw or change Your consent at any time using Our + cookie preferences tool (if available) or through Your browser/device settings. Withdrawing consent + does not affect the lawfulness of processing based on consent before its withdrawal. + + We use both Session and Persistent Cookies for the purposes set out below: + """ + } + + ul(.class("list-disc mx-6 space-y-2")) { + li { + p(.class("font-bold")) { "Necessary / Essential Cookies" } + p(.class("mx-6")) { + """ + Type: Session Cookies + + Administered by: Us + + Purpose: These Cookies are essential to provide You with services available through the Website + and to enable You to use some of its features. They help to authenticate users and prevent + fraudulent use of user accounts. Without these Cookies, the services that You have asked for + cannot be provided, and We only use these Cookies to provide You with those services. + """ + } + } + + li { + p(.class("font-bold")) { "Functionality Cookies" } + p(.class("mx-6")) { + """ + Type: Persistent Cookies + + Administered by: Us + + Purpose: These Cookies allow Us to remember choices You make when You use the Website, such as + remembering your login details or language preference. The purpose of these Cookies is to + provide You with a more personal experience and to avoid You having to re-enter your preferences + every time You use the Website. + """ + } + } + } + + p { + """ + For more information about the cookies we use and your choices regarding cookies, please visit our + Cookies Policy or the Cookies section of Our Privacy Policy. + """ + } + + h3(.class("text-2xl font-bold")) { "Use of Your Personal Data" } + p { + "The Company may use Personal Data for the following purposes:" + } + ul(.class("list-disc mx-6 space-y-2")) { + li { + span(.class("font-bold")) { "To provide and maintain our Service" } + """ + , including to monitor the usage of our Service. + """ + } + li { + span(.class("font-bold")) { "To manage Your Account:" } + """ + to manage Your registration as a user of the Service. The Personal + Data You provide can give You access to different functionalities of the Service that are + available to You as a registered user. + """ + } + li { + span(.class("font-bold")) { "For the performance of a contract:" } + """ + the development, compliance and undertaking of the purchase + contract for the products, items or services You have purchased or of any other contract with Us + through the Service. + """ + } + li { + span(.class("font-bold")) { "To contact You:" } + """ + To contact You by email, telephone calls, SMS, or other equivalent forms of + electronic communication, such as a mobile application's push notifications regarding updates or + informative communications related to the functionalities, products or contracted services, + including the security updates, when necessary or reasonable for their implementation. + """ + } + li { + span(.class("font-bold")) { "To provide You" } + """ + with news, special offers, and general information about other goods, services + and events which We offer that are similar to those that you have already purchased or inquired + about unless You have opted not to receive such information. + """ + } + li { + span(.class("font-bold")) { "To manage Your requests:" } + """ + To attend and manage Your requests to Us. + """ + } + li { + span(.class("font-bold")) { "For business transfers:" } + """ + We may use Your Personal Data to evaluate or conduct a merger, + divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all + of Our assets, whether as a going concern or as part of bankruptcy, liquidation, or similar + proceeding, in which Personal Data held by Us about our Service users is among the assets + transferred. + """ + } + li { + span(.class("font-bold")) { "For other purposes:" } + """ + We may use Your information for other purposes, such as data analysis, + identifying usage trends, determining the effectiveness of our promotional campaigns and to + evaluate and improve our Service, products, services, marketing and your experience. + """ + } + } + p { + "We may share Your Personal Data in the following situations:" + } + ul(.class("list-disc mx-6 space-y-2")) { + li { + span(.class("font-bold")) { "With Service Providers:" } + """ + We may share Your Personal Data with Service Providers to monitor and + analyze the use of our Service, to contact You. + """ + } + li { + span(.class("font-bold")) { "For business transfers:" } + """ + We may share or transfer Your Personal Data in connection with, or + during negotiations of, any merger, sale of Company assets, financing, or acquisition of all or a + portion of Our business to another company. + """ + } + li { + span(.class("font-bold")) { "With Affiliates:" } + """ + We may share Your Personal Data with Our affiliates, in which case we will + require those affiliates to honor this Privacy Policy. Affiliates include Our parent company and + any other subsidiaries, joint venture partners or other companies that We control or that are + under common control with Us. + """ + } + li { + span(.class("font-bold")) { "With business partners:" } + """ + We may share Your Personal Data with Our business partners to offer + You certain products, services or promotions. + """ + } + li { + span(.class("font-bold")) { "With other users:" } + """ + If Our Service offers public areas, when You share Personal Data or + otherwise interact in the public areas with other users, such information may be viewed by all + users and may be publicly distributed outside. + """ + } + li { + span(.class("font-bold")) { "With Your consent:" } + """ + We may disclose Your Personal Data for any other purpose with Your consent. + """ + } + } + + h3(.class("text-2xl font-bold")) { "Retention of Your Personal Data" } + p { + """ + The Company will retain Your Personal Data only for as long as is necessary for the purposes set out + in this Privacy Policy. We will retain and use Your Personal Data to the extent necessary to comply + with our legal obligations (for example, if We are required to retain Your data to comply with + applicable laws), resolve disputes, and enforce our legal agreements and policies. + """ + } + p { + """ + Where possible, We apply shorter retention periods and/or reduce identifiability by deleting, + aggregating, or anonymizing data. Unless otherwise stated, the retention periods below are maximum + periods ("up to") and We may delete or anonymize data sooner when it is no longer needed for the + relevant purpose. We apply different retention periods to different categories of Personal Data + based on the purpose of processing and legal obligations: + """ + } + ul(.class("list-disc mx-6 space-y-2")) { + li { + "Account Information" + ul(.class("list-disc mx-6 space-y-2")) { + li { + """ + User Accounts: retained for the duration of your account relationship plus up to 24 months + after account closure to handle any post-termination issues or resolve disputes. + """ + } + } + } + li { + "Customer Support Data" + ul(.class("list-disc mx-6 space-y-2")) { + li { + """ + Support tickets and correspondence: up to 24 months from the date of ticket closure to resolve + follow-up inquiries, track service quality, and defend against potential legal claims. + """ + } + li { + """ + Chat transcripts: up to 24 months for quality assurance and staff training purposes. + """ + } + } + } + li { + "Usage Data" + ul(.class("list-disc mx-6 space-y-2")) { + li { + """ + Website analytics data (cookies, IP addresses, device identifiers): up to 24 months from the + date of collection, which allows us to analyze trends while respecting privacy principles. + """ + } + li { + """ + Server logs (IP addresses, access times): up to 24 months for security monitoring and + troubleshooting purposes. + """ + } + } + } + } + p { + """ + Usage Data is retained in accordance with the retention periods described above, and may be retained + longer only where necessary for security, fraud prevention, or legal compliance. + """ + } + p { + """ + We may retain Personal Data beyond the periods stated above for different reasons: + """ + } + ul(.class("list-disc mx-6 space-y-2")) { + li { + """ + Legal obligation: We are required by law to retain specific data (e.g., financial records for tax + authorities). + """ + } + li { + """ + Legal claims: Data is necessary to establish, exercise, or defend legal claims. + """ + } + li { + """ + Your explicit request: You ask Us to retain specific information. + """ + } + li { + """ + Technical limitations: Data exists in backup systems that are scheduled for routine deletion. + """ + } + } + p { + """ + You may request information about how long We will retain Your Personal Data by contacting Us. + """ + } + p { + """ + When retention periods expire, We securely delete or anonymize Personal Data according to the + following procedures: + """ + } + ul(.class("list-disc mx-6 space-y-2")) { + li { + """ + Deletion: Personal Data is removed from Our systems and no longer actively processed. + """ + } + li { + """ + Backup retention: Residual copies may remain in encrypted backups for a limited period consistent + with our backup retention schedule and are not restored except where necessary for security, + disaster recovery, or legal compliance. + """ + } + li { + """ + Anonymization: In some cases, We convert Personal Data into anonymous statistical data that cannot + be linked back to You. This anonymized data may be retained indefinitely for research and + analytics. + """ + } + } + + h3(.class("text-xl font-bold")) { "Transfer of Your Personal Data" } + p { + """ + Your information, including Personal Data, is processed at the Company's operating offices and in + any other places where the parties involved in the processing are located. It means that this + information may be transferred to — and maintained on — computers located outside of Your state, + province, country or other governmental jurisdiction where the data protection laws may differ from + those from Your jurisdiction. + """ + } + p { + """ + Where required by applicable law, We will ensure that international transfers of Your Personal Data + are subject to appropriate safeguards and supplementary measures where appropriate. The Company will + take all steps reasonably necessary to ensure that Your data is treated securely and in accordance + with this Privacy Policy and no transfer of Your Personal Data will take place to an organization or + a country unless there are adequate controls in place including the security of Your data and other + personal information. + """ + } + + h3(.class("text-xl font-bold")) { "Delete Your Personal Data" } + p { + """ + You have the right to delete or request that We assist in deleting the Personal Data that We have + collected about You. + """ + } + p { + """ + Our Service may give You the ability to delete certain information about You from within the + Service. + """ + } + p { + """ + You may update, amend, or delete Your information at any time by signing in to Your Account, if you + have one, and visiting the account settings section that allows you to manage Your personal + information. You may also contact Us to request access to, correct, or delete any Personal Data that + You have provided to Us. + """ + } + p { + """ + Please note, however, that We may need to retain certain information when we have a legal obligation + or lawful basis to do so. + """ + } + + h3(.class("text-xl font-bold")) { "Disclosure of Your Personal Data" } + + h4(.class("text-lg font-bold")) { "Business Transactions" } + p { + """ + If the Company is involved in a merger, acquisition or asset sale, Your Personal Data may be + transferred. We will provide notice before Your Personal Data is transferred and becomes subject to + a different Privacy Policy. + """ + } + + h4(.class("text-lg font-bold")) { "Law enforcement" } + p { + """ + Under certain circumstances, the Company may be required to disclose Your Personal Data if required + to do so by law or in response to valid requests by public authorities (e.g. a court or a government + agency). + """ + } + + h4(.class("text-lg font-bold")) { "Other legal requirements" } + p { + """ + The Company may disclose Your Personal Data in the good faith belief that such action is necessary + to: + """ + } + ul(.class("list-disc mx-6 space-y-2")) { + li { "Comply with a legal obligation" } + li { "Protect and defend the rights or property of the Company" } + li { "Prevent or investigate possible wrongdoing in connection with the Service" } + li { "Protect the personal safety of Users of the Service or the public" } + li { "Protect against legal liability" } + } + + h3(.class("text-xl font-bold")) { "Security of Your Personal Data" } + p { + """ + The security of Your Personal Data is important to Us, but remember that no method of transmission + over the Internet, or method of electronic storage is 100% secure. While We strive to use + commercially reasonable means to protect Your Personal Data, We cannot guarantee its absolute + security. + """ + } + + h2(.class("text-2xl font-bold")) { "Children's Privacy" } + p { + """ + Our Service does not address anyone under the age of 16. We do not knowingly collect personally + identifiable information from anyone under the age of 16. If You are a parent or guardian and You + are aware that Your child has provided Us with Personal Data, please contact Us. If We become aware + that We have collected Personal Data from anyone under the age of 16 without verification of + parental consent, We take steps to remove that information from Our servers. + """ + } + p { + """ + If We need to rely on consent as a legal basis for processing Your information and Your country + requires consent from a parent, We may require Your parent's consent before We collect and use that + information. + """ + } + + h2(.class("text-2xl font-bold")) { "Links to Other Websites" } + p { + """ + Our Service may contain links to other websites that are not operated by Us. If You click on a third + party link, You will be directed to that third party's site. We strongly advise You to review the + Privacy Policy of every site You visit. + """ + } + p { + """ + We have no control over and assume no responsibility for the content, privacy policies or practices + of any third party sites or services. + """ + } + + h2(.class("text-2xl font-bold")) { "Changes to this Privacy Policy" } + p { + """ + We may update Our Privacy Policy from time to time. We will notify You of any changes by posting the + new Privacy Policy on this page. + """ + } + p { + """ + We will let You know via email and/or a prominent notice on Our Service, prior to the change + becoming effective and update the "Last updated" date at the top of this Privacy Policy. + """ + } + p { + """ + You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy + Policy are effective when they are posted on this page. + """ + } + + h2(.class("text-2xl font-bold")) { "Contact Us" } + p { + """ + If you have any questions about this Privacy Policy, You can contact us: + """ + } + + ul(.class("list-disc mx-6 space-y-2")) { + li { + span { "By email: " } + a( + .href("mailto://support@ductcalc.pro"), + .class("btn btn-link") + ) { + "support@ductcalc.pro" + } + } + } + + } + } +} diff --git a/Sources/ViewController/Views/User/LoginForm.swift b/Sources/ViewController/Views/User/LoginForm.swift index 11ed2ce..23011bf 100644 --- a/Sources/ViewController/Views/User/LoginForm.swift +++ b/Sources/ViewController/Views/User/LoginForm.swift @@ -22,7 +22,16 @@ struct LoginForm: HTML, Sendable { var body: some HTML { ModalForm(id: "loginForm", closeButton: false, dismiss: false) { - h1(.class("text-2xl font-bold mb-6")) { style.title } + Row { + h1(.class("text-2xl font-bold mb-6")) { style.title } + a( + .class("btn btn-link"), + .href(route: .privacyPolicy), + .target(.blank) + ) { + "Privacy Policy" + } + } form( .method(.post), @@ -78,6 +87,7 @@ struct LoginForm: HTML, Sendable { "At least one uppercase letter" } } + } div(.class("flex")) { diff --git a/TODO.md b/TODO.md index 0f777b7..1caaa1e 100644 --- a/TODO.md +++ b/TODO.md @@ -20,3 +20,6 @@ - [x] Update urls to point to public mirror of repository - [x] Add email to footer - [x] Validation errors, if they occur are vague in ResultView +- [x] Privacy policy +- [ ] Update README +- [ ] Self hosting documentation diff --git a/Tests/ViewControllerTests/ViewControllerTests.swift b/Tests/ViewControllerTests/ViewControllerTests.swift index 31a45ec..093d3d8 100644 --- a/Tests/ViewControllerTests/ViewControllerTests.swift +++ b/Tests/ViewControllerTests/ViewControllerTests.swift @@ -21,7 +21,6 @@ struct ViewControllerTests { $0.auth = .failing } operation: { @Dependency(\.viewController) var viewController - let home = try await viewController.view(.test(.home)) assertSnapshot(of: home, as: .html) } @@ -39,6 +38,18 @@ struct ViewControllerTests { } } + @Test + func privacyPolicy() async throws { + try await withDependencies { + $0.viewController = .liveValue + $0.auth = .failing + } operation: { + @Dependency(\.viewController) var viewController + let view = try await viewController.view(.test(.privacyPolicy)) + assertSnapshot(of: view, as: .html) + } + } + @Test func login() async throws { try await withDependencies { diff --git a/Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/login.1.html b/Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/login.1.html index 9e56b05..853c414 100644 --- a/Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/login.1.html +++ b/Tests/ViewControllerTests/__Snapshots__/ViewControllerTests/login.1.html @@ -33,7 +33,10 @@