actix_web_nextjs/
lib.rs

1pub use error::SpaError;
2pub use spa::{Spa, spa};
3pub use spa_service::SpaService;
4
5mod error;
6mod spa;
7mod spa_service;
8mod utils;
9
10#[cfg(test)]
11mod tests {
12    use std::str::from_utf8;
13
14    use actix_web::{App, body::MessageBody, dev::ServiceFactory, Error, http::StatusCode, test};
15    use actix_web::dev::{ServiceRequest, ServiceResponse};
16
17    use super::*;
18
19    /// Create a test application with SPA service
20    fn test_app() -> App<
21        impl ServiceFactory<
22            ServiceRequest,
23            Response=ServiceResponse<impl MessageBody>,
24            Config=(),
25            InitError=(),
26            Error=Error,
27        >,
28    > {
29        App::new().service(
30            Spa::default()
31                .index_file("./fixtures/001/index.html")
32                .static_resources_location("./fixtures/001")
33                .finish(),
34        )
35    }
36
37    /// Test: Returns the index file for root path
38    #[actix_web::test]
39    async fn returns_index() {
40        let app = test::init_service(test_app()).await;
41
42        let req = test::TestRequest::default().to_request();
43        let res = test::call_service(&app, req).await;
44
45        assert_eq!(res.status(), StatusCode::OK);
46
47        let body = test::read_body(res).await;
48        let html = from_utf8(&body).unwrap();
49        assert!(html.contains("Home page"));
50    }
51
52    /// Test: Returns a specific page for a given path
53    #[actix_web::test]
54    async fn returns_page() {
55        let app = test::init_service(test_app()).await;
56
57        let req = test::TestRequest::default().uri("/page").to_request();
58        let res = test::call_service(&app, req).await;
59
60        assert_eq!(res.status(), StatusCode::OK);
61
62        let body = test::read_body(res).await;
63        let html = from_utf8(&body).unwrap();
64        assert!(html.contains("Sample Page"));
65    }
66
67    /// Test: Returns an item page for a specific path
68    #[actix_web::test]
69    async fn returns_item_page() {
70        let app = test::init_service(test_app()).await;
71
72        let req = test::TestRequest::default().uri("/dog/items/cat").to_request();
73        let res = test::call_service(&app, req).await;
74
75        assert_eq!(res.status(), StatusCode::OK);
76
77        let body = test::read_body(res).await;
78        let html = from_utf8(&body).unwrap();
79        assert!(html.contains("Item Page"));
80    }
81
82    /// Test: Returns the index file for unknown paths
83    #[actix_web::test]
84    async fn unknown_page_returns_index() {
85        let app = test::init_service(test_app()).await;
86
87        let req = test::TestRequest::default().uri("/fsociety").to_request();
88        let res = test::call_service(&app, req).await;
89
90        assert_eq!(res.status(), StatusCode::OK);
91
92        let body = test::read_body(res).await;
93        let html = from_utf8(&body).unwrap();
94        assert!(html.contains("Home page"));
95    }
96
97    /// Test: Returns static assets correctly
98    #[actix_web::test]
99    async fn returns_assets() {
100        let app = test::init_service(test_app()).await;
101
102        let req = test::TestRequest::default().uri("/next.svg").to_request();
103        let res = test::call_service(&app, req).await;
104
105        assert_eq!(res.status(), StatusCode::OK);
106
107        let body = test::read_body(res).await;
108        let svg = from_utf8(&body).unwrap();
109        assert!(svg.contains(r#"<svg xmlns="http://www.w3.org/2000/svg" fill="none""#));
110    }
111
112    /// Test: Returns a dynamic numeric page
113    #[cfg(feature = "wildcards")]
114    #[actix_web::test]
115    async fn test_returns_dynamic_numeric_page() {
116        let app = test::init_service(test_app()).await;
117
118        let req = test::TestRequest::default().uri("/1/items/1").to_request();
119        let res = test::call_service(&app, req).await;
120
121        assert_eq!(res.status(), StatusCode::OK);
122
123        let body = test::read_body(res).await;
124        let html = from_utf8(&body).unwrap();
125        assert!(html.contains("Item Page"));
126    }
127
128    /// Test: Returns a dynamic character page
129    #[cfg(feature = "wildcards")]
130    #[actix_web::test]
131    async fn test_returns_dynamic_character_page() {
132        let app = test::init_service(test_app()).await;
133
134        let req = test::TestRequest::default().uri("/3b2b6d56-e85b-432d-b555-7113b810a3b7/items/1").to_request();
135        let res = test::call_service(&app, req).await;
136
137        assert_eq!(res.status(), StatusCode::OK);
138
139        let body = test::read_body(res).await;
140        let html = from_utf8(&body).unwrap();
141        assert!(html.contains("Item Page"));
142    }
143
144    /// Test: Handles build manifest not found
145    #[cfg(feature = "wildcards")]
146    #[actix_web::test]
147    async fn handles_build_manifest_not_found() {
148        let app = test::init_service(
149            App::new().service(
150                Spa::default()
151                    .index_file("./fixtures/001/index.html")
152                    .static_resources_location("./fixtures/no_manifest")
153                    .finish(),
154            )
155        ).await;
156
157        let req = test::TestRequest::default().uri("/").to_request();
158        let res = test::call_service(&app, req).await;
159
160        assert_eq!(res.status(), StatusCode::OK);
161
162        let body = test::read_body(res).await;
163        let html = from_utf8(&body).unwrap();
164        assert!(html.contains("Home page"));
165    }
166}