Skip to content

Commit 8d9a30d

Browse files
committed
test(term): add subscribe integration test case
1 parent ebadb3c commit 8d9a30d

20 files changed

+2660
-42
lines changed

Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/synd_term/src/application/builder.rs

+17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::{
22
application::{Application, Authenticator, Cache, Config},
33
client::Client,
44
config::Categories,
5+
interact::Interactor,
56
terminal::Terminal,
67
ui::theme::Theme,
78
};
@@ -22,6 +23,7 @@ pub struct ApplicationBuilder<
2223
pub(super) theme: Theme,
2324

2425
pub(super) authenticator: Option<Authenticator>,
26+
pub(super) interactor: Option<Interactor>,
2527
}
2628

2729
impl Default for ApplicationBuilder {
@@ -34,6 +36,7 @@ impl Default for ApplicationBuilder {
3436
config: (),
3537
theme: (),
3638
authenticator: None,
39+
interactor: None,
3740
}
3841
}
3942
}
@@ -49,6 +52,7 @@ impl<T1, T2, T3, T4, T5> ApplicationBuilder<(), T1, T2, T3, T4, T5> {
4952
config: self.config,
5053
theme: self.theme,
5154
authenticator: self.authenticator,
55+
interactor: self.interactor,
5256
}
5357
}
5458
}
@@ -64,6 +68,7 @@ impl<T1, T2, T3, T4, T5> ApplicationBuilder<T1, (), T2, T3, T4, T5> {
6468
config: self.config,
6569
theme: self.theme,
6670
authenticator: self.authenticator,
71+
interactor: self.interactor,
6772
}
6873
}
6974
}
@@ -82,6 +87,7 @@ impl<T1, T2, T3, T4, T5> ApplicationBuilder<T1, T2, (), T3, T4, T5> {
8287
config: self.config,
8388
theme: self.theme,
8489
authenticator: self.authenticator,
90+
interactor: self.interactor,
8591
}
8692
}
8793
}
@@ -97,6 +103,7 @@ impl<T1, T2, T3, T4, T5> ApplicationBuilder<T1, T2, T3, (), T4, T5> {
97103
config: self.config,
98104
theme: self.theme,
99105
authenticator: self.authenticator,
106+
interactor: self.interactor,
100107
}
101108
}
102109
}
@@ -112,6 +119,7 @@ impl<T1, T2, T3, T4, T5> ApplicationBuilder<T1, T2, T3, T4, (), T5> {
112119
config,
113120
theme: self.theme,
114121
authenticator: self.authenticator,
122+
interactor: self.interactor,
115123
}
116124
}
117125
}
@@ -127,6 +135,7 @@ impl<T1, T2, T3, T4, T5> ApplicationBuilder<T1, T2, T3, T4, T5, ()> {
127135
config: self.config,
128136
theme,
129137
authenticator: self.authenticator,
138+
interactor: self.interactor,
130139
}
131140
}
132141
}
@@ -139,6 +148,14 @@ impl<T1, T2, T3, T4, T5, T6> ApplicationBuilder<T1, T2, T3, T4, T5, T6> {
139148
..self
140149
}
141150
}
151+
152+
#[must_use]
153+
pub fn interactor(self, interactor: Interactor) -> Self {
154+
Self {
155+
interactor: Some(interactor),
156+
..self
157+
}
158+
}
142159
}
143160

144161
impl ApplicationBuilder<Terminal, Client, Categories, Cache, Config, Theme> {

crates/synd_term/src/application/mod.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ impl Application {
128128
config,
129129
theme,
130130
authenticator,
131+
interactor,
131132
} = builder;
132133

133134
let key_handlers = {
@@ -146,7 +147,7 @@ impl Application {
146147
client,
147148
jobs: Jobs::new(),
148149
components: Components::new(),
149-
interactor: Interactor::new(),
150+
interactor: interactor.unwrap_or_else(Interactor::new),
150151
authenticator: authenticator.unwrap_or_else(Authenticator::new),
151152
in_flight: InFlight::new().with_throbber_timer_interval(config.throbber_timer_interval),
152153
cache,
@@ -1019,4 +1020,12 @@ impl Application {
10191020
self.reset_idle_timer();
10201021
}
10211022
}
1023+
1024+
pub async fn reload_cache(&mut self) -> anyhow::Result<()> {
1025+
match self.restore_credential().await {
1026+
Ok(cred) => self.handle_initial_credential(cred),
1027+
Err(err) => return Err(err.into()),
1028+
}
1029+
Ok(())
1030+
}
10221031
}

crates/synd_term/src/interact/integration_interactor.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@ use std::ffi::OsStr;
22

33
pub type Interactor = TestInteractor;
44

5-
pub struct TestInteractor;
5+
pub struct TestInteractor {
6+
editor_buffer: String,
7+
}
68

79
impl TestInteractor {
810
pub fn new() -> Self {
9-
Self {}
11+
Self {
12+
editor_buffer: String::new(),
13+
}
14+
}
15+
16+
pub fn with_buffer(mut self, editor_buffer: impl Into<String>) -> Self {
17+
self.editor_buffer = editor_buffer.into();
18+
self
1019
}
1120

1221
#[allow(clippy::unused_self, clippy::needless_pass_by_value)]
@@ -16,6 +25,6 @@ impl TestInteractor {
1625

1726
#[allow(clippy::unused_self, clippy::needless_pass_by_value)]
1827
pub fn open_editor<S: AsRef<[u8]>>(&self, _initial_content: S) -> String {
19-
String::new()
28+
self.editor_buffer.clone()
2029
}
2130
}

crates/synd_term/src/keymap/macros.rs

+10
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,14 @@ macro_rules! key {
4242
crossterm::event::KeyCode::Enter,
4343
))
4444
};
45+
( tab ) => {
46+
crossterm::event::Event::Key(crossterm::event::KeyEvent::from(
47+
crossterm::event::KeyCode::Tab,
48+
))
49+
};
50+
( $char:literal ) => {
51+
crossterm::event::Event::Key(crossterm::event::KeyEvent::from(
52+
crossterm::event::KeyCode::Char($char),
53+
))
54+
};
4555
}

crates/synd_term/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![warn(rustdoc::broken_intra_doc_links)]
33

44
pub mod application;
5-
pub(crate) mod auth;
5+
pub mod auth;
66
pub mod cli;
77
pub mod client;
88
pub(crate) mod command;

crates/synd_term/tests/integration.rs

+77-9
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,26 @@
22
mod test {
33
use std::path::Path;
44

5-
use serial_test::file_serial;
65
use synd_term::key;
7-
use tokio_stream::wrappers::UnboundedReceiverStream;
86

97
mod helper;
108
use crate::test::helper::TestCase;
119

1210
#[tokio::test(flavor = "multi_thread")]
13-
#[file_serial(a)]
14-
async fn happy() -> anyhow::Result<()> {
11+
async fn login() -> anyhow::Result<()> {
1512
helper::init_tracing();
1613

1714
let test_case = TestCase {
18-
oauth_provider_port: 6000,
15+
mock_port: 6000,
1916
synd_api_port: 6001,
2017
kvsd_port: 47379,
2118
terminal_col_row: (120, 30),
2219
device_flow_case: "case1",
2320
cache_dir: helper::temp_dir().into_path(),
21+
..Default::default()
2422
};
2523
let mut application = test_case.init_app().await?;
26-
27-
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
28-
let mut event_stream = UnboundedReceiverStream::new(rx);
24+
let (tx, mut event_stream) = helper::event_stream();
2925

3026
{
3127
application
@@ -40,7 +36,7 @@ mod test {
4036

4137
{
4238
// push enter => start auth flow
43-
tx.send(Ok(key!(enter))).unwrap();
39+
tx.send(key!(enter));
4440
application.event_loop_until_idle(&mut event_stream).await;
4541
insta::with_settings!({
4642
description => "show device flow code",
@@ -107,4 +103,76 @@ mod test {
107103
.assert()
108104
.success();
109105
}
106+
107+
#[tokio::test(flavor = "multi_thread")]
108+
async fn subscribe_then_unsubscribe() -> anyhow::Result<()> {
109+
helper::init_tracing();
110+
111+
let test_case = TestCase {
112+
mock_port: 6010,
113+
synd_api_port: 6011,
114+
kvsd_port: 47389,
115+
terminal_col_row: (120, 30),
116+
interactor_buffer: Some("should rust http://localhost:6010/feed/twir_atom".into()),
117+
..Default::default()
118+
}
119+
.already_logined();
120+
121+
let mut application = test_case.init_app().await?;
122+
let (tx, mut event_stream) = helper::event_stream();
123+
124+
{
125+
// Move tab to feeds
126+
tx.send(key!(tab));
127+
application
128+
.wait_until_jobs_completed(&mut event_stream)
129+
.await;
130+
insta::with_settings!({
131+
description => "after feeds tab move",
132+
},{
133+
insta::assert_debug_snapshot!("subscribe_then_unsubscribe_landing_feeds", application.buffer());
134+
});
135+
}
136+
137+
{
138+
// Subscribe
139+
tx.send(key!('a'));
140+
application
141+
.wait_until_jobs_completed(&mut event_stream)
142+
.await;
143+
insta::with_settings!({
144+
description => "after parsing editor buffer for subscribe",
145+
},{
146+
insta::assert_debug_snapshot!("subscribe_then_unsubscribe_after_editor_parse", application.buffer());
147+
});
148+
}
149+
150+
{
151+
// Unsubscribe popup
152+
tx.send(key!('d'));
153+
application
154+
.wait_until_jobs_completed(&mut event_stream)
155+
.await;
156+
insta::with_settings!({
157+
description => "unsubscribe popup",
158+
},{
159+
insta::assert_debug_snapshot!("subscribe_then_unsubscribe_unsubscribe_popup", application.buffer());
160+
});
161+
}
162+
163+
{
164+
// Select Yes (assuming Yes is selected)
165+
tx.send(key!(enter));
166+
application
167+
.wait_until_jobs_completed(&mut event_stream)
168+
.await;
169+
insta::with_settings!({
170+
description => "after unsubscribe",
171+
},{
172+
insta::assert_debug_snapshot!("subscribe_then_unsubscribe_unsubscribed", application.buffer());
173+
});
174+
}
175+
176+
Ok(())
177+
}
110178
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
source: crates/synd_term/tests/integration.rs
3+
description: after parsing editor buffer for subscribe
4+
expression: application.buffer()
5+
---
6+
Buffer {
7+
area: Rect { x: 0, y: 0, width: 120, height: 30 },
8+
content: [
9+
" Syndicationd 󱉯 Entries 󰑫 Feeds ",
10+
" 󰈶 Filter MAY  ",
11+
"  Search ",
12+
" ",
13+
" Updated Feed 1/1 URL Description Req ",
14+
" 2024-05-29  This Week in Rust this-week-in-rust.org SHD▐",
15+
"",
16+
"",
17+
"",
18+
"",
19+
"",
20+
"",
21+
"",
22+
"",
23+
"",
24+
"",
25+
"",
26+
"",
27+
"",
28+
"",
29+
"────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────",
30+
" 󰚼 Authors - 󰗀 Src http://localhost:6010/feed/twir_atom ",
31+
"  Generator - 󰈙 Type Atom ",
32+
"  Category rust  Req SHOULD ",
33+
" ",
34+
" Published Entry Summary ",
35+
" 2024-05-29 This Week in Rust 549 Hello and welcome to another issue of *This Week in Rust*! [Rust][1] ",
36+
" 2024-05-22 This Week in Rust 548 Hello and welcome to another issue of *This Week in Rust*! [Rust][1] ",
37+
" 2024-05-15 This Week in Rust 547 Hello and welcome to another issue of *This Week in Rust*! [Rust][1] ",
38+
" Tab:󰹳 j/k:󰹹 gg:󱞧 ge:󱞥 h/l: c: /: r:󰑓 Ent:󰏌 a:󰑫 e: d:󰼡 q: ",
39+
],
40+
styles: [
41+
x: 0, y: 0, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
42+
x: 2, y: 0, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
43+
x: 14, y: 0, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
44+
x: 112, y: 0, fg: Rgb(255, 160, 122), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
45+
x: 119, y: 0, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
46+
x: 2, y: 1, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: DIM,
47+
x: 10, y: 1, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
48+
x: 14, y: 1, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: DIM,
49+
x: 17, y: 1, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
50+
x: 20, y: 1, fg: Rgb(247, 76, 0), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
51+
x: 21, y: 1, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
52+
x: 2, y: 2, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: DIM,
53+
x: 10, y: 2, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
54+
x: 0, y: 4, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD | UNDERLINED,
55+
x: 0, y: 5, fg: Rgb(255, 160, 122), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
56+
x: 119, y: 5, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
57+
x: 0, y: 6, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
58+
x: 2, y: 21, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
59+
x: 11, y: 21, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
60+
x: 49, y: 21, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
61+
x: 56, y: 21, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
62+
x: 2, y: 22, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
63+
x: 13, y: 22, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
64+
x: 49, y: 22, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
65+
x: 56, y: 22, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
66+
x: 2, y: 23, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
67+
x: 12, y: 23, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
68+
x: 49, y: 23, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD,
69+
x: 56, y: 23, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
70+
x: 2, y: 25, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: BOLD | UNDERLINED,
71+
x: 118, y: 25, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
72+
x: 23, y: 29, fg: Rgb(111, 93, 99), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
73+
x: 98, y: 29, fg: Rgb(254, 205, 178), bg: Rgb(43, 41, 45), underline: Reset, modifier: NONE,
74+
]
75+
}

0 commit comments

Comments
 (0)