Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
595f1f6ef8 | ||
|
|
c8e9adb81d | ||
|
|
6d3a7545a6 | ||
|
|
6739af2be1 | ||
|
|
c41a7aaacf | ||
|
|
d200c409d6 | ||
|
|
cd28dee7bd | ||
|
|
7e82a08b54 | ||
|
|
f0bd50b246 | ||
|
|
e571eb9881 | ||
|
|
c4f4bcf457 | ||
|
|
9500abb363 | ||
|
|
690a3746fa | ||
|
|
cd82c13753 | ||
|
|
032a648204 | ||
|
|
f64ee912d0 | ||
|
|
307556b7dd | ||
|
|
dd8b9dd43e | ||
|
|
6e41359fc5 | ||
|
|
b1cc4e485b | ||
|
|
239bf2fa70 | ||
|
|
a6baabbafc | ||
|
|
a954b5ffd8 | ||
|
|
36b88c602c | ||
|
|
190fae196f | ||
|
|
347698f68f | ||
|
|
db2e23172a | ||
|
|
dcb070909c | ||
|
|
332a677d20 | ||
|
|
75d394fb6e | ||
|
|
875b9d6cfc | ||
|
|
e9326d49c0 | ||
|
|
4ee6e297c6 | ||
|
|
c4ebccafd4 | ||
|
|
94bdefbf01 | ||
|
|
ba482a30a3 | ||
|
|
76416cfdba | ||
|
|
2557e4f3cc | ||
|
|
79c912219d | ||
|
|
89370b4a55 | ||
|
|
ecbc2601c4 | ||
|
|
f3753435e6 | ||
|
|
1eeb812a1c | ||
|
|
6dba118413 | ||
|
|
ee7fc5d77d | ||
|
|
e7533c149f | ||
|
|
bdec917a35 | ||
|
|
76a3daecaf | ||
|
|
ed1952d6a9 | ||
|
|
5a87229fe9 | ||
|
|
eac878d563 | ||
|
|
de08c01c4c | ||
|
|
7937fd1f3a | ||
|
|
eef5c2bf66 | ||
|
|
9c769cd7e5 | ||
|
|
eac58dc212 | ||
|
|
d3241cdecb | ||
|
|
857f645131 | ||
|
|
2521611d6a | ||
|
|
5facb61e23 | ||
|
|
2f6a4f8c7b | ||
|
|
84b8b92582 | ||
|
|
827d7c381e | ||
|
|
b0bf99da34 | ||
|
|
ca50e56474 | ||
|
|
36cf653d64 | ||
|
|
8ccd1ec72c | ||
|
|
7ba3020ab3 | ||
|
|
50694bb589 | ||
|
|
206ccb2c43 | ||
|
|
d2ccec79e4 | ||
|
|
69469e9989 | ||
|
|
c821b8c4fe | ||
|
|
93786bad44 | ||
|
|
f5bfcdcb98 | ||
|
|
d57b61b213 | ||
|
|
43a3a1e4b2 | ||
|
|
65f3296508 | ||
|
|
5cede74c89 | ||
|
|
54fcc4ac5b | ||
|
|
2ca911950c | ||
|
|
462b67def9 | ||
|
|
30a28b12d1 | ||
|
|
0b3c904984 | ||
|
|
1e16168a3d | ||
|
|
f5db384fc2 | ||
|
|
ce58737ec5 | ||
|
|
ca7b186325 | ||
|
|
d05b633b9b | ||
|
|
1cde5a0df6 | ||
|
|
df8409178b | ||
|
|
121ed5a06e | ||
|
|
29e80a0f32 | ||
|
|
a49b009980 | ||
|
|
9b55f5bc7f | ||
|
|
0371a28a42 | ||
|
|
ce51491cf6 | ||
|
|
c033d56978 | ||
|
|
a4a21c7e94 | ||
|
|
851296fa0e | ||
|
|
c3b183504f | ||
|
|
363bb3e45e | ||
|
|
acc93a603d | ||
|
|
8045042e78 | ||
|
|
1401ba5960 | ||
|
|
0730a0caf4 | ||
|
|
13260e1db8 | ||
|
|
4fa08fda02 | ||
|
|
2042394501 | ||
|
|
bbeae74ebd | ||
|
|
3aad1c5bcd | ||
|
|
f760e3d12a | ||
|
|
890285bb7b | ||
|
|
366e28752c | ||
|
|
6ecc3c9076 | ||
|
|
f5a9f2bf4d | ||
|
|
d68c847d59 | ||
|
|
9a1ea138fc | ||
|
|
b466b425a5 | ||
|
|
15aefaa956 | ||
|
|
e7bdce0997 | ||
|
|
44e48712a6 | ||
|
|
7d512578b2 | ||
|
|
0783f94cd6 | ||
|
|
eef34caf27 | ||
|
|
5ef9d55dca | ||
|
|
781fbf1b30 | ||
|
|
60ba775bed | ||
|
|
d862455342 | ||
|
|
d26606d3d3 | ||
|
|
6cece56ca7 | ||
|
|
2b096e967d | ||
|
|
7884fd1913 | ||
|
|
3f9b598493 | ||
|
|
d5b9063f78 | ||
|
|
b80b222527 | ||
|
|
c175947818 | ||
|
|
b7b8713cd9 | ||
|
|
81e09ae72e | ||
|
|
4e6643eca8 | ||
|
|
7586930ef0 | ||
|
|
e9a4efb67d | ||
|
|
eff06a1f74 | ||
|
|
775955df5c | ||
|
|
8cf6ab32f0 | ||
|
|
177cca60c4 | ||
|
|
649545cdf3 | ||
|
|
2a421034d9 | ||
|
|
ef49cb6577 | ||
|
|
6338ca1417 | ||
|
|
acb3c795d0 | ||
|
|
4405b597cd | ||
|
|
a7ce21e941 | ||
|
|
05bdd052ab | ||
|
|
d86431dfbc | ||
|
|
4837d7f7fe | ||
|
|
d765806337 | ||
|
|
fe5ce75ea8 | ||
|
|
99b2b2712b | ||
|
|
96af012471 | ||
|
|
7ad05feb5b | ||
|
|
69d3181a64 | ||
|
|
109ebaf989 | ||
|
|
1a13de2ee9 | ||
|
|
48d6967d3b | ||
|
|
7d55153475 | ||
|
|
d25830aece | ||
|
|
04eea26a55 | ||
|
|
25e94da7e7 | ||
|
|
2d7a373d77 | ||
|
|
1491fbfb0d | ||
|
|
0d776a9294 | ||
|
|
d4cef2e866 | ||
|
|
a070ed2441 | ||
|
|
5fa441cbf5 | ||
|
|
4ae679128f | ||
|
|
4d71b42a3d | ||
|
|
ff3ff9f888 | ||
|
|
147080ad27 | ||
|
|
bb4b987681 | ||
|
|
415f121720 | ||
|
|
9824d5980a | ||
|
|
5ff6405f7f | ||
|
|
a4e0c5754f | ||
|
|
5f94ba129e | ||
|
|
af07b53484 | ||
|
|
b43b522d97 | ||
|
|
0683483fd4 | ||
|
|
afab3d5374 | ||
|
|
066bba95f5 | ||
|
|
d004c94a2f | ||
|
|
c0ae803280 | ||
|
|
d7a67c0efb | ||
|
|
b307dd30d8 | ||
|
|
068245eec9 | ||
|
|
6b4d4fef15 | ||
|
|
64aa2ba7e4 | ||
|
|
8665a25b39 | ||
|
|
551e012147 | ||
|
|
7da759c662 | ||
|
|
8036d070ad | ||
|
|
a5ed145aa3 | ||
|
|
20b485bcfe | ||
|
|
38e3947b2d | ||
|
|
94f9349aba | ||
|
|
a9ea8fd159 | ||
|
|
1e6762b424 | ||
|
|
7c54b6d599 | ||
|
|
3e17613f54 | ||
|
|
9d61ca0278 | ||
|
|
3595de5e12 | ||
|
|
32ca864bf7 | ||
|
|
bfa9233bd4 | ||
|
|
c659cf4ff2 | ||
|
|
cac3acd553 | ||
|
|
771dd9979f | ||
|
|
929508df16 | ||
|
|
79b1515c3a | ||
|
|
7fb1b4f43f | ||
|
|
b3dd5fb8bd | ||
|
|
29a6eab1ce | ||
|
|
c475a5d960 | ||
|
|
414bb26e56 | ||
|
|
1fad713477 | ||
|
|
e18e031710 | ||
|
|
bddaac05ae | ||
|
|
3e5fae002b | ||
|
|
66835a5409 | ||
|
|
8afbf28430 | ||
|
|
0427402ba7 | ||
|
|
6973d66263 | ||
|
|
5fea801a88 | ||
|
|
8b81889bcf | ||
|
|
800094587b | ||
|
|
de75e028df | ||
|
|
9b3c1921e8 | ||
|
|
412f34c72a | ||
|
|
a7364e1f4a | ||
|
|
16467e9c13 | ||
|
|
b00d29d0cd | ||
|
|
acc951c5eb | ||
|
|
b9ecc7ceb6 | ||
|
|
6f83f41c2d | ||
|
|
8179419f8b | ||
|
|
4a1a143a66 | ||
|
|
21bbf87089 | ||
|
|
19d9e7717c | ||
|
|
d190850f03 | ||
|
|
68de83c611 | ||
|
|
729b8571b5 | ||
|
|
31e9a0b4ea | ||
|
|
6a9866b9d2 | ||
|
|
1c500fbfc5 | ||
|
|
4992f9c281 | ||
|
|
75c26c807c | ||
|
|
e788c71602 | ||
|
|
7de2131431 | ||
|
|
47f3702ca9 | ||
|
|
3708a5b86a | ||
|
|
a63a7ea611 | ||
|
|
7867f0366e | ||
|
|
251c6d22b2 | ||
|
|
7cee8e392f | ||
|
|
765103dd22 | ||
|
|
50e612511d | ||
|
|
7eb20b232a | ||
|
|
3c7e636b65 | ||
|
|
889a313cc2 | ||
|
|
9586ea0e6f | ||
|
|
42e7317cd4 | ||
|
|
17651bf64c | ||
|
|
73f1c211c2 | ||
|
|
afc6473582 | ||
|
|
663f9d3423 | ||
|
|
5d4142d5b6 | ||
|
|
fe23183aef | ||
|
|
8116ea1dba | ||
|
|
3e6529aeb8 | ||
|
|
c4fc60612f | ||
|
|
4d82bb5322 | ||
|
|
2b684ae7bf | ||
|
|
eb54cf99d4 | ||
|
|
85af82acbe | ||
|
|
3434d447a7 | ||
|
|
1025b6d553 | ||
|
|
b35d6131d4 | ||
|
|
bc502c9c5b | ||
|
|
5f49c1748f | ||
|
|
73ea1a0685 | ||
|
|
a93d325e6a | ||
|
|
dc0af0f4c1 | ||
|
|
1ad85416d8 | ||
|
|
c34a7a92fb | ||
|
|
21d34d1fe0 | ||
|
|
3e088d48ac | ||
|
|
54dbcb7c5e | ||
|
|
59dff54edb | ||
|
|
d358e18f33 | ||
|
|
dd9b1fb78f | ||
|
|
e08a1a6974 | ||
|
|
7f4e7e6aa0 | ||
|
|
18bd91936c | ||
|
|
b9a25f449f | ||
|
|
993532a2f0 | ||
|
|
8a735799c6 | ||
|
|
197da5f425 | ||
|
|
e28e5a0649 | ||
|
|
09b4e19c9d | ||
|
|
653c831473 | ||
|
|
a2f2cdebb7 | ||
|
|
59e0a1325e | ||
|
|
c0446d7a96 | ||
|
|
1fbee02731 | ||
|
|
102fdbce5c | ||
|
|
71c6beb882 | ||
|
|
ce83042317 | ||
|
|
03598d3f22 | ||
|
|
c2eb2014a3 | ||
|
|
9416ba1bfc | ||
|
|
8c3984d21a | ||
|
|
1883dd6d7c | ||
|
|
a354f9fe52 | ||
|
|
41e2c74b5e | ||
|
|
74d9d427bd | ||
|
|
aa27c4e6d4 | ||
|
|
f40ae9ff25 | ||
|
|
e66087861b | ||
|
|
147637fcf5 | ||
|
|
48582cb718 | ||
|
|
ad21a61ffb | ||
|
|
d34eec1d99 | ||
|
|
95aeac201f | ||
|
|
cceee80799 | ||
|
|
71f80be8f7 | ||
|
|
16a4babdca | ||
|
|
85c0622245 | ||
|
|
e3410ec568 | ||
|
|
c0b0f12b0d | ||
|
|
70a2b7d9ab | ||
|
|
a84c47dc33 | ||
|
|
56c5d69b0f | ||
|
|
6c3d7d2079 | ||
|
|
25a98cda81 | ||
|
|
cd6e133c86 | ||
|
|
b16a102f85 | ||
|
|
d51c7f382f | ||
|
|
a57ca5c0cb | ||
|
|
1e9137fcfe | ||
|
|
8e5b6b6e27 | ||
|
|
7e101b8bf1 | ||
|
|
bf0cfa798c | ||
|
|
1793a28cbc | ||
|
|
e565d0ee32 | ||
|
|
f60462a853 | ||
|
|
966547faa0 | ||
|
|
3647af44c9 | ||
|
|
25b4a647b0 | ||
|
|
c6e3c1fd7c | ||
|
|
4fd35d0de4 | ||
|
|
7406c5e6a5 | ||
|
|
aa70885b21 | ||
|
|
8699c9c125 | ||
|
|
bc758e0b06 | ||
|
|
e38980ccc3 | ||
|
|
8dd180046f | ||
|
|
0cd2726752 | ||
|
|
11daa15b89 | ||
|
|
8436bc6144 | ||
|
|
59851f46b7 | ||
|
|
62f1d6eb82 | ||
|
|
ec80f72c8f | ||
|
|
b2c80981cc | ||
|
|
8fa425a76c | ||
|
|
ebe112581d | ||
|
|
bc8f35d7db | ||
|
|
058eeec932 | ||
|
|
8e10cba8e5 | ||
|
|
7a24f6b6f0 | ||
|
|
4f40945458 | ||
|
|
e59870e421 | ||
|
|
cf521fe20b | ||
|
|
f0d47ee50c | ||
|
|
2a5d393c9f | ||
|
|
98e0b832ec | ||
|
|
77e7cbe9ec | ||
|
|
acd53eb640 | ||
|
|
ffdf5d24a0 | ||
|
|
b247a49417 | ||
|
|
dace02d34d | ||
|
|
41d4351176 | ||
|
|
d4bc575c03 | ||
|
|
a59561aee3 | ||
|
|
92523321f1 | ||
|
|
537c9d0865 | ||
|
|
b4051c7e9f | ||
|
|
9e61133886 | ||
|
|
36f47f3636 | ||
|
|
40dcd13394 | ||
|
|
eb20c7c117 | ||
|
|
e9852a3934 | ||
|
|
78d8ec9a3e | ||
|
|
4220309cf8 | ||
|
|
812af0979e | ||
|
|
cca5005541 | ||
|
|
9306d4a4ee | ||
|
|
3ab1ff5e73 | ||
|
|
05fa306782 | ||
|
|
d054439df4 | ||
|
|
15aaacc4b7 | ||
|
|
ef9de52cbc | ||
|
|
8d9d0e9358 | ||
|
|
7f44b181bd | ||
|
|
01c3fec5aa | ||
|
|
94a3a59001 | ||
|
|
4a433a3176 | ||
|
|
f7749bdccc | ||
|
|
c864e836ee | ||
|
|
0738b5f87d | ||
|
|
6198c3907f | ||
|
|
891d794900 | ||
|
|
890f52a00a | ||
|
|
d007f98af6 | ||
|
|
15e9d65798 | ||
|
|
0626f0deb7 | ||
|
|
007c649c31 | ||
|
|
34ea508f5b | ||
|
|
f70aef68be | ||
|
|
61a31c90ee | ||
|
|
c7e3ed805a | ||
|
|
d36a846425 | ||
|
|
a02ec98e9c | ||
|
|
32c06cf1bf | ||
|
|
cf1096b516 | ||
|
|
938953d7b3 | ||
|
|
949dab9afe | ||
|
|
0996f5fd24 | ||
|
|
6e887f1f12 | ||
|
|
ab9338eb89 | ||
|
|
fd4088aed0 | ||
|
|
375ff5ed6d | ||
|
|
847290d9a4 | ||
|
|
56687581f2 | ||
|
|
3be39f5e7b | ||
|
|
1997231adf | ||
|
|
89face4f02 | ||
|
|
37ddce3a29 | ||
|
|
593df96b10 | ||
|
|
1b966171c4 | ||
|
|
047f1a0b39 | ||
|
|
f2f3506b29 | ||
|
|
0ccced4b54 | ||
|
|
f762a564b5 | ||
|
|
57b78ee3c6 | ||
|
|
5dfc470873 | ||
|
|
d5f8ce5435 | ||
|
|
3c65a96b04 | ||
|
|
6ba7fc0317 | ||
|
|
37a5054ebd | ||
|
|
048434024b | ||
|
|
2c757e9b6c | ||
|
|
9c59e1487e | ||
|
|
4b9b723521 | ||
|
|
a389836897 | ||
|
|
eec7723ce0 | ||
|
|
2f938a331f | ||
|
|
847d7cb35a | ||
|
|
094f8fc307 | ||
|
|
e3f11ea9c0 | ||
|
|
124d435f09 | ||
|
|
b700f95c7f | ||
|
|
daf9b48c73 | ||
|
|
0ea5b3c483 | ||
|
|
f3148640eb | ||
|
|
1c84ee1d53 | ||
|
|
cecd4f48ab | ||
|
|
1e001640aa | ||
|
|
3426e5b53e | ||
|
|
936b49a63f | ||
|
|
f6f80b48c9 | ||
|
|
759cb27a43 | ||
|
|
26a2bffcd1 | ||
|
|
fe55452c79 | ||
|
|
16adacac38 | ||
|
|
d86531ac92 | ||
|
|
9231455f0d | ||
|
|
0c6a1d4ae7 | ||
|
|
524efc2620 | ||
|
|
50a4f03931 | ||
|
|
a4681a4042 | ||
|
|
1ca130d7de | ||
|
|
ef6f9a3175 | ||
|
|
aa25aa4474 | ||
|
|
c7af04b317 | ||
|
|
9565173baf | ||
|
|
5f4e3ecc76 | ||
|
|
043c68a333 | ||
|
|
f639d51c11 | ||
|
|
71071b60b2 | ||
|
|
608255fa0f | ||
|
|
f8b0a26acc | ||
|
|
133dab934b | ||
|
|
a81670c447 | ||
|
|
35f84e42cc | ||
|
|
2f6df6a17d | ||
|
|
8bcc6384f2 | ||
|
|
a0fc4b86b0 | ||
|
|
790580eea4 | ||
|
|
3db9024a2d | ||
|
|
a821740469 | ||
|
|
af0ae2e375 | ||
|
|
7d6fa0b159 | ||
|
|
b73a34ed94 | ||
|
|
f3aaff77b9 | ||
|
|
9b7ff375b2 | ||
|
|
18498c0479 | ||
|
|
c652c32575 | ||
|
|
df8642d446 | ||
|
|
abb6eff23d | ||
|
|
3b4f8e53a2 | ||
|
|
4847b8b114 | ||
|
|
e8f124b97c | ||
|
|
f73d59fb57 | ||
|
|
897d7980f6 | ||
|
|
b88aed887a | ||
|
|
4e863bc87f | ||
|
|
507554a596 | ||
|
|
0806436c19 | ||
|
|
1205f0ebf7 | ||
|
|
26e56194ea | ||
|
|
4e67282f2b | ||
|
|
41ec2d992e | ||
|
|
f0e4f3db61 | ||
|
|
6634a0ecb3 | ||
|
|
255f30590f | ||
|
|
1ba7224088 |
No files matched your search
@@ -44,7 +44,7 @@ test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||
if [ $? -ne 0 ]
|
||||
then
|
||||
printError "Please fix your commit message to match AppFlowy coding standards"
|
||||
printError "https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/submitting-code/code-submission-guidelines#commit-message-guidelines"
|
||||
printError "https://docs.appflowy.io/docs/documentation/software-contributions/conventions/git-conventions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -21,7 +21,7 @@ Before you mark this PR ready for review, run through this checklist!
|
||||
|
||||
#### PR Checklist
|
||||
|
||||
- [ ] My code adheres to the [AppFlowy Style Guide](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/submitting-code/style-guides)
|
||||
- [ ] My code adheres to [AppFlowy's Conventions](https://docs.appflowy.io/docs/documentation/software-contributions/conventions)
|
||||
- [ ] I've listed at least one issue that this PR fixes in the description above.
|
||||
- [ ] I've added a test(s) to validate changes in this PR, or this PR only contains semantic changes.
|
||||
- [ ] All existing tests are passing.
|
||||
@@ -0,0 +1,99 @@
|
||||
name: Build AppFlowy Release APK
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-linux-android
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
# the following step is required to avoid running out of space
|
||||
- name: Maximize build space
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: "r24"
|
||||
add-to-path: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
version: "0.36.6"
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
rustup target install aarch64-linux-android
|
||||
rustup target install x86_64-linux-android
|
||||
cargo install --force duckscript_cli
|
||||
cargo install cargo-ndk
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0 libnotify-dev
|
||||
sudo apt-get install gcc-multilib
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
run: |
|
||||
cargo make --profile production-android appflowy-android
|
||||
|
||||
- name: Upload APK
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-arm64-v8a-release.apk
|
||||
path: frontend/appflowy_flutter/build/app/outputs/flutter-apk/
|
||||
@@ -4,33 +4,390 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "develop"
|
||||
- "release/*"
|
||||
paths:
|
||||
- ".github/workflows/flutter_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
- "frontend/rust-lib/**"
|
||||
- "frontend/appflowy_flutter/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "develop"
|
||||
- "release/*"
|
||||
paths:
|
||||
- ".github/workflows/flutter_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
- "frontend/rust-lib/**"
|
||||
- "frontend/appflowy_flutter/**"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: macos-latest
|
||||
flutter_profile: development-mac-x86_64
|
||||
target: x86_64-apple-darwin
|
||||
- os: windows-latest
|
||||
flutter_profile: development-windows-x86
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
# the following step is required to avoid running out of space
|
||||
- name: Maximize build space
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
cache-all-crates: true
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}, duckscript_cli
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: cargo make --profile ${{ matrix.flutter_profile }} appflowy-core-dev
|
||||
|
||||
- name: Run code generation
|
||||
working-directory: frontend
|
||||
run: cargo make code_generation
|
||||
|
||||
- name: Flutter Analyzer
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: flutter analyze .
|
||||
|
||||
- name: Compress appflowy_flutter
|
||||
run: |
|
||||
tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
path: appflowy_flutter.tar.gz
|
||||
|
||||
unit_test:
|
||||
needs: [prepare]
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
cache-all-crates: true
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}, duckscript_cli
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
flutter config --enable-linux-desktop
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter config --enable-macos-desktop
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
git config --system core.longpaths true
|
||||
flutter config --enable-windows-desktop
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompress appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
- name: Run flutter pub get
|
||||
working-directory: frontend
|
||||
run: cargo make pub_get
|
||||
|
||||
- name: Run Flutter unit tests
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "macOS" ]; then
|
||||
cargo make dart_unit_test
|
||||
elif [ "$RUNNER_OS" == "Linux" ]; then
|
||||
cargo make dart_unit_test_no_build
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
cargo make dart_unit_test_no_build
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
cloud_integration_test:
|
||||
needs: [ prepare ]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ ubuntu-latest ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout appflowy cloud code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: AppFlowy-IO/AppFlowy-Cloud
|
||||
path: AppFlowy-Cloud
|
||||
depth: 1
|
||||
|
||||
- name: Prepare appflowy cloud env
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
# log level
|
||||
cp deploy.env .env
|
||||
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
|
||||
sed -i 's/GOTRUE_EXTERNAL_GOOGLE_ENABLED=.*/GOTRUE_EXTERNAL_GOOGLE_ENABLED=true/' .env
|
||||
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
|
||||
- name: Run Docker-Compose
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
docker compose down -v --remove-orphans
|
||||
docker pull appflowyinc/appflowy_cloud:latest
|
||||
docker compose up -d
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
run: |
|
||||
flutter config --enable-linux-desktop
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompressed appflowy_flutter
|
||||
run: |
|
||||
tar -xf appflowy_flutter.tar.gz
|
||||
ls -al
|
||||
|
||||
- name: Run flutter pub get
|
||||
working-directory: frontend
|
||||
run: cargo make pub_get
|
||||
|
||||
- name: Run Flutter integration tests
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
export DISPLAY=:99
|
||||
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
|
||||
sudo apt-get install network-manager
|
||||
flutter test integration_test/cloud/cloud_runner.dart -d Linux --coverage
|
||||
shell: bash
|
||||
|
||||
integration_test:
|
||||
needs: [prepare]
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
flutter config --enable-linux-desktop
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter config --enable-macos-desktop
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
git config --system core.longpaths true
|
||||
flutter config --enable-windows-desktop
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompressed appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
- name: Run flutter pub get
|
||||
working-directory: frontend
|
||||
run: cargo make pub_get
|
||||
|
||||
- name: Run Flutter integration tests
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
export DISPLAY=:99
|
||||
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
|
||||
sudo apt-get install network-manager
|
||||
flutter test integration_test/runner.dart -d Linux --coverage
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter test integration_test/runner.dart -d macOS --coverage
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
flutter test integration_test/runner.dart -d Windows --coverage
|
||||
fi
|
||||
shell: bash
|
||||
build:
|
||||
needs: [prepare]
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -65,36 +422,22 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
version: '0.36.6'
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force duckscript_cli
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
@@ -109,32 +452,14 @@ jobs:
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
|
||||
|
||||
- name: Flutter Analyzer
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
flutter analyze .
|
||||
|
||||
- name: Run Flutter unit tests
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make dart_unit_test
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: Wandalen/wretry.action@v1.0.36
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
action: codecov/codecov-action@v3
|
||||
with: |
|
||||
name: appflowy
|
||||
flags: appflowy_flutter_unit_test
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
os: ${{ matrix.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
attempt_limit: 20
|
||||
attempt_delay: 10000
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompressed appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
- name: Build flutter product
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-make-product-dev
|
||||
@@ -1,141 +0,0 @@
|
||||
name: integration test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- ".github/workflows/integration_test.yml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/*"
|
||||
paths:
|
||||
- ".github/workflows/integration_test.yml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
- os: windows-latest
|
||||
flutter_profile: development-windows-x86
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
cache-all-crates: true
|
||||
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
version: '0.36.6'
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force duckscript_cli
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
flutter config --enable-linux-desktop
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter config --enable-macos-desktop
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
git config --system core.longpaths true
|
||||
flutter config --enable-windows-desktop
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
|
||||
|
||||
- name: Run Flutter integration tests
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
export DISPLAY=:99
|
||||
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
|
||||
sudo apt-get install network-manager
|
||||
flutter test integration_test/runner.dart -d Linux --coverage
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
flutter test integration_test/runner.dart -d macOS --coverage
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
flutter test integration_test/runner.dart -d Windows --coverage
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: Wandalen/wretry.action@v1.0.36
|
||||
with:
|
||||
action: codecov/codecov-action@v3
|
||||
with: |
|
||||
name: appflowy
|
||||
flags: appflowy_flutter_integrateion_test
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
os: ${{ matrix.os }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
attempt_limit: 20
|
||||
attempt_delay: 10000
|
||||
@@ -0,0 +1,117 @@
|
||||
name: Mobile-CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
paths:
|
||||
- ".github/workflows/mobile_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
paths:
|
||||
- ".github/workflows/mobile_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-linux-android
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
# the following step is required to avoid running out of space
|
||||
- name: Maximize build space
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: "r24"
|
||||
add-to-path: true
|
||||
|
||||
- uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
gradle-version: 7.6.3
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- uses: davidB/rust-cargo-make@v1
|
||||
with:
|
||||
version: "0.36.6"
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
rustup target install aarch64-linux-android
|
||||
rustup target install x86_64-linux-android
|
||||
cargo install --force duckscript_cli
|
||||
cargo install cargo-ndk
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0 libnotify-dev
|
||||
sudo apt-get install gcc-multilib
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
run: |
|
||||
cargo make --profile development-android appflowy-android-dev-ci
|
||||
@@ -6,8 +6,8 @@ on:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
@@ -35,13 +35,15 @@ jobs:
|
||||
release_name: v${{ github.ref }}
|
||||
body_path: ${{ env.RELEASE_NOTES_PATH }}
|
||||
|
||||
# the package name should be with the format: AppFlowy-<version>-<os>-<arch>
|
||||
|
||||
build-for-windows:
|
||||
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
|
||||
needs: create-release
|
||||
env:
|
||||
WINDOWS_APP_RELEASE_PATH: frontend\appflowy_flutter\product\${{ github.ref_name }}\windows
|
||||
WINDOWS_ZIP_NAME: AppFlowy_${{ github.ref_name }}_windows-x86_64.zip
|
||||
WINDOWS_INSTALLER_NAME: AppFlowy_${{ github.ref_name }}_windows-x86_64
|
||||
WINDOWS_ZIP_NAME: AppFlowy-${{ github.ref_name }}-windows-x86_64.zip
|
||||
WINDOWS_INSTALLER_NAME: AppFlowy-${{ github.ref_name }}-windows-x86_64
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -55,9 +57,8 @@ jobs:
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -68,11 +69,6 @@ jobs:
|
||||
components: rustfmt
|
||||
profile: minimal
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: appflowy-lib-cache
|
||||
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
@@ -133,8 +129,8 @@ jobs:
|
||||
needs: create-release
|
||||
env:
|
||||
MACOS_APP_RELEASE_PATH: frontend/appflowy_flutter/product/${{ github.ref_name }}/macos/Release
|
||||
MACOS_X86_ZIP_NAME: AppFlowy_${{ github.ref_name }}_macos-x86_64.zip
|
||||
MACOS_DMG_NAME: AppFlowy_${{ github.ref_name }}_macos-x86_64
|
||||
MACOS_X86_ZIP_NAME: AppFlowy-${{ github.ref_name }}-macos-x86_64.zip
|
||||
MACOS_DMG_NAME: AppFlowy-${{ github.ref_name }}-macos-x86_64
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -147,9 +143,8 @@ jobs:
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -160,11 +155,6 @@ jobs:
|
||||
components: rustfmt
|
||||
profile: minimal
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: appflowy-lib-cache
|
||||
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
@@ -235,8 +225,8 @@ jobs:
|
||||
needs: create-release
|
||||
env:
|
||||
MACOS_APP_RELEASE_PATH: frontend/appflowy_flutter/product/${{ github.ref_name }}/macos/Release
|
||||
MACOS_AARCH64_ZIP_NAME: AppFlowy_${{ github.ref_name }}_macos-universal.zip
|
||||
MACOS_DMG_NAME: AppFlowy_${{ github.ref_name }}_macos-universal
|
||||
MACOS_AARCH64_ZIP_NAME: AppFlowy-${{ github.ref_name }}-macos-universal.zip
|
||||
MACOS_DMG_NAME: AppFlowy-${{ github.ref_name }}-macos-universal
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -253,9 +243,8 @@ jobs:
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@@ -264,11 +253,6 @@ jobs:
|
||||
targets: ${{ matrix.job.targets }}
|
||||
components: rustfmt
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: appflowy-lib-cache
|
||||
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
@@ -279,7 +263,7 @@ jobs:
|
||||
working-directory: frontend
|
||||
run: |
|
||||
flutter config --enable-macos-desktop
|
||||
sh scripts/flutter_release_build/build_universal_package_for_macos.sh
|
||||
sh scripts/flutter_release_build/build_universal_package_for_macos.sh ${{ github.ref_name }}
|
||||
|
||||
- name: Codesign AppFlowy
|
||||
run: |
|
||||
@@ -339,9 +323,11 @@ jobs:
|
||||
needs: create-release
|
||||
env:
|
||||
LINUX_APP_RELEASE_PATH: frontend/appflowy_flutter/product/${{ github.ref_name }}/linux/Release
|
||||
LINUX_ZIP_NAME: AppFlowy_${{ matrix.job.target }}_${{ matrix.job.os }}.tar.gz
|
||||
LINUX_PACKAGE_NAME: AppFlowy_${{ github.ref_name }}_${{ matrix.job.os }}.deb
|
||||
# PKG_CONFIG_SYSROOT_DIR: /
|
||||
LINUX_ZIP_NAME: AppFlowy-${{ matrix.job.target }}-x86_64.tar.gz
|
||||
LINUX_PACKAGE_DEB_NAME: AppFlowy-${{ github.ref_name }}-linux-x86_64.deb
|
||||
LINUX_PACKAGE_RPM_NAME: AppFlowy-${{ github.ref_name }}-linux-x86_64.rpm
|
||||
LINUX_PACKAGE_TMP_RPM_NAME: AppFlowy-${{ github.ref_name }}-2.x86_64.rpm
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -360,9 +346,8 @@ jobs:
|
||||
- name: Install flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -373,18 +358,14 @@ jobs:
|
||||
components: rustfmt
|
||||
profile: minimal
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: appflowy-lib-cache
|
||||
key: ${{ matrix.job.os }}-${{ matrix.job.target }}
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0
|
||||
sudo apt-get install keybinder-3.0 libnotify-dev
|
||||
sudo apt-get -y install alien
|
||||
source $HOME/.cargo/env
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
@@ -409,8 +390,17 @@ jobs:
|
||||
- name: Build Linux package (.deb)
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sh scripts/linux_distribution/deb/build_deb.sh appflowy_flutter/product/${{ github.ref_name }}/linux/Release ${{ github.ref_name }} ${{ env.LINUX_PACKAGE_NAME }}
|
||||
|
||||
sh scripts/linux_distribution/deb/build_deb.sh appflowy_flutter/product/${{ github.ref_name }}/linux/Release ${{ github.ref_name }} ${{ env.LINUX_PACKAGE_DEB_NAME }}
|
||||
|
||||
- name: Build Linux package (.rpm)
|
||||
working-directory: ${{ env.LINUX_APP_RELEASE_PATH }}
|
||||
run: |
|
||||
ls -a
|
||||
sudo alien -r ${{ env.LINUX_PACKAGE_DEB_NAME }}
|
||||
echo '------------'
|
||||
cp -r ${{ env.LINUX_PACKAGE_TMP_RPM_NAME }} ${{ env.LINUX_PACKAGE_RPM_NAME }}
|
||||
ls -a
|
||||
|
||||
- name: Upload Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
@@ -423,15 +413,25 @@ jobs:
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload Debian package
|
||||
id: upload-release-asset-install-package
|
||||
id: upload-release-asset-install-package-deb
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_NAME }}
|
||||
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_DEB_NAME }}
|
||||
asset_name: ${{ env.LINUX_PACKAGE_DEB_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
asset_name: ${{ env.LINUX_PACKAGE_NAME }}
|
||||
- name: Upload RPM package
|
||||
id: upload-release-asset-install-package-rpm
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: ${{ env.LINUX_APP_RELEASE_PATH }}/${{ env.LINUX_PACKAGE_RPM_NAME }}
|
||||
asset_name: ${{ env.LINUX_PACKAGE_RPM_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-for-docker:
|
||||
|
||||
@@ -8,7 +8,6 @@ on:
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -17,15 +16,14 @@ on:
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
|
||||
jobs:
|
||||
test-on-ubuntu:
|
||||
environment: SUPABASE_CI
|
||||
# environment: SUPABASE_CI
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
@@ -52,30 +50,58 @@ jobs:
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
- name: Create .env file in flowy-server
|
||||
working-directory: frontend/rust-lib/flowy-server
|
||||
run: |
|
||||
touch .env.ci
|
||||
echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
# - name: Create .env file in flowy-server
|
||||
# working-directory: frontend/rust-lib/flowy-server
|
||||
# run: |
|
||||
# touch .env.ci
|
||||
# echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
# echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
# echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
#
|
||||
# - name: Create .env file in event-integration
|
||||
# working-directory: frontend/rust-lib/event-integration
|
||||
# run: |
|
||||
# touch .env.ci
|
||||
# echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
# echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
# echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
|
||||
- name: Create .env file in flowy-test
|
||||
working-directory: frontend/rust-lib/flowy-test
|
||||
- name: Checkout appflowy cloud code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: AppFlowy-IO/AppFlowy-Cloud
|
||||
path: AppFlowy-Cloud
|
||||
depth: 1
|
||||
|
||||
- name: Prepare appflowy cloud env
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
touch .env.ci
|
||||
echo SUPABASE_URL=${{ secrets.SUPABASE_URL }} >> .env.ci
|
||||
echo SUPABASE_ANON_KEY=${{ secrets.SUPABASE_ANON_KEY }} >> .env.ci
|
||||
echo SUPABASE_JWT_SECRET=${{ secrets.SUPABASE_JWT_SECRET }} >> .env.ci
|
||||
# log level
|
||||
cp deploy.env .env
|
||||
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
|
||||
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
|
||||
- name: Run Docker-Compose
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
docker pull appflowyinc/appflowy_cloud:latest
|
||||
docker compose up -d
|
||||
|
||||
- name: Run rust-lib tests
|
||||
working-directory: frontend/rust-lib
|
||||
run: RUST_LOG=info RUST_BACKTRACE=1 cargo test --no-default-features --features="rev-sqlite"
|
||||
env:
|
||||
RUST_LOG: info
|
||||
RUST_BACKTRACE: 1
|
||||
af_cloud_test_base_url: http://localhost
|
||||
af_cloud_test_ws_url: ws://localhost/ws
|
||||
af_cloud_test_gotrue_url: http://localhost/gotrue
|
||||
run: cargo test --no-default-features --features="rev-sqlite" -- --nocapture
|
||||
|
||||
|
||||
- name: rustfmt rust-lib
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: frontend/rust-lib/
|
||||
|
||||
- name: clippy rust-lib
|
||||
run: cargo clippy --all
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
working-directory: frontend/rust-lib
|
||||
@@ -7,12 +7,11 @@ on:
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
@@ -34,7 +33,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -32,6 +32,26 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Cache Rust Dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: rust-dependencies-${{ runner.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
frontend/appflowy_tauri/src-tauri
|
||||
|
||||
- name: Cache Node.js dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: npm-${{ runner.os }}
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: frontend/appflowy_tauri/node_modules
|
||||
key: node-modules-${{ runner.os }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
@@ -40,13 +60,6 @@ jobs:
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: 'ubuntu-latest-tauri'
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
frontend/appflowy_tauri/src-tauri
|
||||
|
||||
- name: install dependencies (windows only)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
working-directory: frontend
|
||||
@@ -75,15 +88,23 @@ jobs:
|
||||
cargo make appflowy-tauri-deps-tools
|
||||
npm install -g pnpm@${{ env.PNPM_VERSION }}
|
||||
|
||||
|
||||
- name: build
|
||||
- name: Build
|
||||
working-directory: frontend/appflowy_tauri
|
||||
run: |
|
||||
mkdir dist
|
||||
pnpm install
|
||||
cargo make --cwd .. tauri_build
|
||||
pnpm test
|
||||
pnpm test:errors
|
||||
|
||||
- name: Check for uncommitted changes
|
||||
run: |
|
||||
diff_files=$(git status --porcelain)
|
||||
if [ -n "$diff_files" ]; then
|
||||
echo "There are uncommitted changes in the working tree. Please commit them before pushing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,82 @@
|
||||
name: WEB-CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "main"
|
||||
paths:
|
||||
- ".github/workflows/web_ci.yaml"
|
||||
- "frontend/rust-lib/**"
|
||||
- "frontend/appflowy_web/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
jobs:
|
||||
web-build:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ubuntu-latest]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Cache Rust Dependencies
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: rust-dependencies-${{ runner.os }}
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
frontend/appflowy_web/appflowy_wasm
|
||||
|
||||
- name: Cache Node.js dependencies
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: npm-${{ runner.os }}
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: frontend/appflowy_web/node_modules
|
||||
key: node-modules-${{ runner.os }}
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install wasm-pack
|
||||
run: cargo install wasm-pack
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: install dependencies
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo apt-get update
|
||||
npm install -g pnpm@${{ env.PNPM_VERSION }}
|
||||
cargo make install_web_protobuf
|
||||
|
||||
- name: Build
|
||||
working-directory: frontend/appflowy_web
|
||||
run: |
|
||||
pnpm install
|
||||
pnpm run wasm
|
||||
@@ -21,11 +21,8 @@ node_modules
|
||||
|
||||
**/resources/proto
|
||||
|
||||
!frontend/.vscode/settings.json
|
||||
!frontend/.vscode/tasks.json
|
||||
!frontend/.vscode/launch.json
|
||||
!frontend/.vscode/extensions.json
|
||||
!frontend/.vscode/*.code-snippets
|
||||
# ignore settings.json
|
||||
frontend/.vscode/settings.json
|
||||
|
||||
# Commit the highest level pubspec.lock, but ignore the others
|
||||
pubspec.lock
|
||||
@@ -38,6 +35,8 @@ pubspec.lock
|
||||
|
||||
**/AppFlowy-Collab/
|
||||
|
||||
# ignore the deb filegit
|
||||
# ignore generated assets
|
||||
frontend/package
|
||||
frontend/*.deb
|
||||
|
||||
**/Cargo.toml.bak
|
||||
@@ -1,23 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ProtoBuf_Gen" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --manifest-path ${flowy_tool} -- pb-gen --rust_sources=${rust_lib},${shared_lib} --derive_meta=${derive_meta} --flutter_package_lib=${flutter_package_lib}" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/frontend" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs>
|
||||
<env name="flowy_tool" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/scripts/flowy-tool/Cargo.toml" />
|
||||
<env name="rust_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/" />
|
||||
<env name="shared_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/../shared_lib" />
|
||||
<env name="flutter_lib" value="${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/appflowy_flutter/packages" />
|
||||
<env name="derive_meta" value="${shared_lib}/flowy-derive/src/derive_cache/derive_cache.rs" />
|
||||
<env name="flutter_package_lib" value="${flutter_lib}/flowy_sdk/lib" />
|
||||
</envs>
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,18 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run backend" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --package backend --bin backend" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$/backend" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs>
|
||||
<env name="APP_ENVIRONMENT" value="production" />
|
||||
</envs>
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,16 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="dart-event" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --manifest-path $PROJECT_DIR$/scripts/flowy-tool/Cargo.toml -- dart-event --rust_source=$PROJECT_DIR$/rust-lib/ --output=$PROJECT_DIR$/appflowy_flutter/packages/flowy_sdk/lib/dispatch/dart_event.dart" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,5 +1,152 @@
|
||||
# Release Notes
|
||||
|
||||
## Version 0.4.1 - 01/03/2024
|
||||
### Bug fixes
|
||||
- Fix import AppFlowy data folder
|
||||
|
||||
## Version 0.4.0 - 12/30/2023
|
||||
1. Added capability to import data from an AppFlowy data folder. For detailed information, please see [AppFlowy Data Storage Documentation](https://docs.appflowy.io/docs/appflowy/product/data-storage).
|
||||
2. Enhanced user interface and fixed various bugs.
|
||||
3. Improved the efficiency of data synchronization in AppFlowy Cloud
|
||||
|
||||
## Version 0.3.9.1 - 12/07/2023
|
||||
|
||||
### Bug fixes
|
||||
- Fix potential blank pages that may occur in an empty document
|
||||
|
||||
## Version 0.3.9 - 12/07/2023
|
||||
|
||||
### New Features
|
||||
- Support inserting a new field to the left or right of an existing one
|
||||
|
||||
### Bug fixes
|
||||
- Fix some emojis are shown in black/white
|
||||
- Fix unable to rename a subpage of subpage
|
||||
|
||||
## Version 0.3.8 - 11/13/2023
|
||||
|
||||
### New Features
|
||||
- Support hiding any stack in a board
|
||||
- Support customizing page icons in menu
|
||||
- Display visual hint when card contains notes
|
||||
- Quick action for adding new stack to a board
|
||||
- Support more ways of inserting page references in documents
|
||||
- Shift + click on a checkbox to power toggle its children
|
||||
|
||||
### Bug fixes
|
||||
- Improved color of the "Share"-button text
|
||||
- Text overflow issue in Calendar properties
|
||||
- Default font (Roboto) added to application
|
||||
- Placeholder added for the editor inside a Card
|
||||
- Toggle notifications in settings have been fixed
|
||||
- Dialog for linking board/grid/calendar opens in correct position
|
||||
- Quick add Card in Board at top, correctly adds a new Card at the top
|
||||
|
||||
## Version 0.3.7 - 10/30/2023
|
||||
|
||||
### New Features
|
||||
- Support showing checklist items inline in row page.
|
||||
- Support inserting date from slash menu.
|
||||
- Support renaming a stack directly by clicking on the stack name.
|
||||
- Show the detailed reminder content in the notification center.
|
||||
- Save card order in Board view.
|
||||
- Allow to hide the ungrouped stack.
|
||||
- Segmented the checklist progress bar.
|
||||
|
||||
### Bug fixes
|
||||
- Optimize side panel animation.
|
||||
- Fix calendar with hidden date or title doesn't show options correctly.
|
||||
- Fix the horizontal scroll bar disappears in Grid view.
|
||||
- Improve setting tab UI in Grid view.
|
||||
- Improve theme of the code block.
|
||||
- Fix some UI issues.
|
||||
|
||||
## Version 0.3.6 - 10/16/2023
|
||||
|
||||
### New Features
|
||||
- Support setting Markdown styles through keyboard shortcuts.
|
||||
- Added Ukrainian language.
|
||||
- Support auto-hiding sidebar feature, ensuring a streamlined view even when resizing to a smaller window.
|
||||
- Support toggling the notifitcation on/off.
|
||||
- Added Lemonade theme.
|
||||
|
||||
### Bug fixes
|
||||
- Improve Vietnamese translations.
|
||||
- Improve reminder feature.
|
||||
- Fix some UI issues.
|
||||
|
||||
## Version 0.3.5 - 10/09/2023
|
||||
|
||||
### New Features
|
||||
- Added support for browsing and inserting images from Unsplash.
|
||||
- Revamp and unify the emoji picker throughout AppFlowy.
|
||||
|
||||
### Bug fixes
|
||||
- Improve layout of the settings page.
|
||||
- Improve design of the restore page banner.
|
||||
- Improve UX of the reminders.
|
||||
- Other UI fixes.
|
||||
|
||||
## Version 0.3.4 - 10/02/2023
|
||||
|
||||
### New Features
|
||||
- Added support for creating a reminder.
|
||||
- Added support for finding and replacing in the document page.
|
||||
- Added support for showing the hidden fields in row detail page.
|
||||
- Adjust the toolbar style in RTL mode.
|
||||
|
||||
### Bug fixes
|
||||
- Improve snackbar UI design.
|
||||
- Improve dandelion theme.
|
||||
- Improve id-ID and pl-PL language translations.
|
||||
|
||||
## Version 0.3.3 - 09/24/2023
|
||||
|
||||
### New Features
|
||||
- Added an end date field to the time cell in the database.
|
||||
- Added Support for customizing the font family from GoogleFonts in the editor.
|
||||
- Set the uploaded image to cover by default.
|
||||
- Added Support for resetting the user icon on settings page
|
||||
- Add Urdu language translations.
|
||||
|
||||
### Bug fixes
|
||||
- Default colors for the blocks except for the callout were not transparent.
|
||||
- Option/Alt + click to add a block above didn't work on the first line.
|
||||
- Unable to paste HTML content containing `<mark>` tag.
|
||||
- Unable to select the text from anywhere in the line.
|
||||
- The selection in the editor didn't clear when editing the inline database.
|
||||
- Added a bottom border to new property column in the database.
|
||||
- Set minimum width of 50px for grid fields.
|
||||
|
||||
## Version 0.3.2 - 09/18/2023
|
||||
|
||||
### New Features
|
||||
|
||||
- Improve the performance of the editor, now it is much faster when editing a large document.
|
||||
- Support for reordering the rows of the database on Windows.
|
||||
- Revamp the row detail page of the database.
|
||||
- Revamp the checklist cell editor of the database.
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Some UI issues
|
||||
|
||||
## Version 0.3.1 - 09/04/2023
|
||||
|
||||
### New Features
|
||||
|
||||
- Improve CJK (Chinese, Japanese, Korean) input method support.
|
||||
- Share a database in CSV format.
|
||||
- Support for aligning the block component with the toolbar.
|
||||
- Support for editing name when creating a new page.
|
||||
- Support for inserting a table in the document page.
|
||||
- Database views allow for independent field visibility toggling.
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Paste multiple lines in code block.
|
||||
- Some UI issues
|
||||
|
||||
## Version 0.3.0 - 08/22/2023
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -18,7 +18,7 @@ You are in charge of your data and customizations.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="http://www.appflowy.io"><b>Website</b></a> •
|
||||
<a href="https://www.appflowy.io"><b>Website</b></a> •
|
||||
<a href="https://discord.gg/9Q2xaN37tV"><b>Discord</b></a> •
|
||||
<a href="https://twitter.com/appflowy"><b>Twitter</b></a>
|
||||
</p>
|
||||
@@ -47,7 +47,7 @@ You are in charge of your data and customizations.
|
||||
|
||||
## Getting Started with development
|
||||
|
||||
Please view the [documentation](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy) for OS specific development instructions
|
||||
Please view the [documentation](https://docs.appflowy.io/docs/documentation/appflowy/from-source) for OS specific development instructions
|
||||
|
||||
## Roadmap
|
||||
|
||||
@@ -73,7 +73,7 @@ Proudly wear your T-shirt and show it to us by tagging [@appflowy](https://twitt
|
||||
|
||||
[](https://inlang.com/editor/github.com/AppFlowy-IO/AppFlowy?ref=badge)
|
||||
|
||||
To add translations, you can manually edit the JSON translation files in `/frontend/appflowy_flutter/assets/translations`, use the [inlang online editor](https://inlang.com/editor/github.com/AppFlowy-IO/AppFlowy), or run `npx inlang machine translate` to add missing translations.
|
||||
To add translations, you can manually edit the JSON translation files in `/frontend/resources/translations`, use the [inlang online editor](https://inlang.com/editor/github.com/AppFlowy-IO/AppFlowy), or run `npx inlang machine translate` to add missing translations.
|
||||
|
||||
|
||||
## Join the community to build AppFlowy together
|
||||
|
||||
@@ -4,6 +4,21 @@
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// This task only builds the Dart code of AppFlowy.
|
||||
// It supports both the desktop and mobile version.
|
||||
"name": "AF: Build Dart Only",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "trace",
|
||||
"RUST_BACKTRACE": "1"
|
||||
},
|
||||
// uncomment the following line to testing performance.
|
||||
// "flutterMode": "profile",
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
// This task builds the Rust and Dart code of AppFlowy.
|
||||
"name": "AF-desktop: Build All",
|
||||
@@ -13,18 +28,7 @@
|
||||
"preLaunchTask": "AF: Build Appflowy Core",
|
||||
"env": {
|
||||
"RUST_LOG": "trace",
|
||||
"RUST_BACKTRACE": 1
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
// This task only builds the Dart code of AppFlowy.
|
||||
"name": "AF-desktop: Build Dart Only",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug",
|
||||
"RUST_BACKTRACE": "1"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
@@ -43,6 +47,17 @@
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-iOS: Build All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: Build Appflowy Core For iOS",
|
||||
"env": {
|
||||
"RUST_LOG": "trace"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-iOS: Clean + Rebuild All",
|
||||
"request": "launch",
|
||||
@@ -54,6 +69,17 @@
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-iOS-Simulator: Build All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: Build Appflowy Core For iOS Simulator",
|
||||
"env": {
|
||||
"RUST_LOG": "trace"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-iOS-Simulator: Clean + Rebuild All",
|
||||
"request": "launch",
|
||||
@@ -65,6 +91,28 @@
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-Android: Build All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: Build Appflowy Core For Android",
|
||||
"env": {
|
||||
"RUST_LOG": "trace"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-Android: Clean + Rebuild All",
|
||||
"request": "launch",
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"preLaunchTask": "AF: Clean + Rebuild All (Android)",
|
||||
"env": {
|
||||
"RUST_LOG": "trace"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"name": "AF-desktop: Debug Rust",
|
||||
"request": "attach",
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"[dart]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
"editor.rulers": [80],
|
||||
"editor.selectionHighlight": false,
|
||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||
"editor.suggestSelection": "first",
|
||||
"editor.tabCompletion": "onlySnippets",
|
||||
"editor.wordBasedSuggestions": false,
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [80],
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.rulers": [80],
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"svgviewer.enableautopreview": true,
|
||||
"svgviewer.previewcolumn": "Active",
|
||||
"svgviewer.showzoominout": true,
|
||||
"editor.wordWrapColumn": 80,
|
||||
"editor.minimap.maxColumn": 140,
|
||||
"editor.wordWrap": "wordWrapColumn",
|
||||
"dart.lineLength": 80,
|
||||
"typescript.validate.enable": true,
|
||||
"javascript.validate.enable": true,
|
||||
"files.associations": {
|
||||
"*.log.*": "log"
|
||||
},
|
||||
"editor.formatOnSave": true,
|
||||
"files.eol": "\n",
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
"AF: Flutter Clean",
|
||||
"AF: Build Appflowy Core",
|
||||
"AF: Flutter Pub Get",
|
||||
"AF: Flutter Package Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Svg Files"
|
||||
@@ -37,7 +36,6 @@
|
||||
"AF: Flutter Clean",
|
||||
"AF: Build Appflowy Core For iOS",
|
||||
"AF: Flutter Pub Get",
|
||||
"AF: Flutter Package Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Svg Files"
|
||||
@@ -56,7 +54,6 @@
|
||||
"AF: Flutter Clean",
|
||||
"AF: Build Appflowy Core For iOS Simulator",
|
||||
"AF: Flutter Pub Get",
|
||||
"AF: Flutter Package Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Svg Files"
|
||||
@@ -66,6 +63,41 @@
|
||||
"panel": "new"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Clean + Rebuild All (Android)",
|
||||
"type": "shell",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": [
|
||||
"AF: Dart Clean",
|
||||
"AF: Flutter Clean",
|
||||
"AF: Build Appflowy Core For Android",
|
||||
"AF: Flutter Pub Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Svg Files"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Build Appflowy Core",
|
||||
"type": "shell",
|
||||
"windows": {
|
||||
"command": "cargo make --profile development-windows-x86 appflowy-core-dev"
|
||||
},
|
||||
"linux": {
|
||||
"command": "cargo make --profile \"development-linux-$(uname -m)\" appflowy-core-dev"
|
||||
},
|
||||
"osx": {
|
||||
"command": "cargo make --profile \"development-mac-$(uname -m)\" appflowy-core-dev"
|
||||
},
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Build Appflowy Core For iOS",
|
||||
"type": "shell",
|
||||
@@ -85,17 +117,9 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Build Appflowy Core",
|
||||
"label": "AF: Build Appflowy Core For Android",
|
||||
"type": "shell",
|
||||
"windows": {
|
||||
"command": "cargo make --profile development-windows-x86 appflowy-core-dev"
|
||||
},
|
||||
"linux": {
|
||||
"command": "cargo make --profile \"development-linux-$(uname -m)\" appflowy-core-dev"
|
||||
},
|
||||
"osx": {
|
||||
"command": "cargo make --profile \"development-mac-$(uname -m)\" appflowy-core-dev"
|
||||
},
|
||||
"command": "cargo make --profile development-android appflowy-core-dev-android",
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
@@ -108,7 +132,6 @@
|
||||
"dependsOn": [
|
||||
"AF: Flutter Clean",
|
||||
"AF: Flutter Pub Get",
|
||||
"AF: Flutter Package Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Svg Files"
|
||||
@@ -122,6 +145,15 @@
|
||||
"panel": "new"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Dart Clean",
|
||||
"type": "shell",
|
||||
"command": "cargo make flutter_clean",
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Flutter Clean",
|
||||
"type": "shell",
|
||||
@@ -138,14 +170,6 @@
|
||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Flutter Package Get",
|
||||
"type": "shell",
|
||||
"command": "flutter packages pub get",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Generate Freezed Files",
|
||||
"type": "shell",
|
||||
@@ -209,34 +233,18 @@
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Flutter Clean",
|
||||
"type": "shell",
|
||||
"command": "cargo make flutter_clean",
|
||||
"group": "build",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: flutter build aar",
|
||||
"type": "flutter",
|
||||
"command": "flutter",
|
||||
"args": ["build", "aar"],
|
||||
"args": [
|
||||
"build",
|
||||
"aar"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"detail": "appflowy_flutter"
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri UI Dev",
|
||||
"type": "shell",
|
||||
"isBackground": true,
|
||||
"command": "yarn",
|
||||
"args": ["dev"],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri UI Build",
|
||||
"type": "shell",
|
||||
@@ -246,9 +254,10 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Tauri Dev",
|
||||
"label": "AF: Tauri UI Dev",
|
||||
"type": "shell",
|
||||
"command": "npm run tauri:dev",
|
||||
"isBackground": true,
|
||||
"command": "pnpm run tauri:dev",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_tauri"
|
||||
}
|
||||
@@ -265,7 +274,10 @@
|
||||
"label": "AF: Tauri Clean + Dev",
|
||||
"type": "shell",
|
||||
"dependsOrder": "sequence",
|
||||
"dependsOn": ["AF: Tauri Clean", "AF: Tauri UI Dev"],
|
||||
"dependsOn": [
|
||||
"AF: Tauri Clean",
|
||||
"AF: Tauri UI Dev"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
@@ -281,10 +293,10 @@
|
||||
{
|
||||
"label": "AF: Generate Env File",
|
||||
"type": "shell",
|
||||
"command": "dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs ",
|
||||
"command": "dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -20,13 +20,15 @@ run_task = { name = ["restore-crate-type"] }
|
||||
|
||||
[env]
|
||||
RUST_LOG = "info"
|
||||
CARGO_PROFILE = "dev"
|
||||
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||
LIB_NAME = "dart_ffi"
|
||||
CURRENT_APP_VERSION = "0.3.0"
|
||||
APPFLOWY_VERSION = "0.4.2"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
|
||||
# If you update the macOS's CRATE_TYPE, don't forget to update the
|
||||
# appflowy_backend.podspec
|
||||
@@ -75,6 +77,7 @@ BUILD_ACTIVE_ARCHS_ONLY = true
|
||||
CRATE_TYPE = "staticlib"
|
||||
|
||||
[env.production-mac-arm64]
|
||||
CARGO_PROFILE = "release"
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "macos"
|
||||
RUST_COMPILE_TARGET = "aarch64-apple-darwin"
|
||||
@@ -86,6 +89,7 @@ BUILD_ACTIVE_ARCHS_ONLY = false
|
||||
CRATE_TYPE = "staticlib"
|
||||
|
||||
[env.production-mac-x86_64]
|
||||
CARGO_PROFILE = "release"
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "macos"
|
||||
RUST_COMPILE_TARGET = "x86_64-apple-darwin"
|
||||
@@ -97,6 +101,7 @@ BUILD_ACTIVE_ARCHS_ONLY = false
|
||||
CRATE_TYPE = "staticlib"
|
||||
|
||||
[env.production-mac-universal]
|
||||
CARGO_PROFILE = "release"
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "macos"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
@@ -114,6 +119,7 @@ CRATE_TYPE = "cdylib"
|
||||
LIB_EXT = "dll"
|
||||
|
||||
[env.production-windows-x86]
|
||||
CARGO_PROFILE = "release"
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "windows"
|
||||
RUST_COMPILE_TARGET = "x86_64-pc-windows-msvc"
|
||||
@@ -121,6 +127,7 @@ FLUTTER_OUTPUT_DIR = "Release"
|
||||
PRODUCT_EXT = "exe"
|
||||
CRATE_TYPE = "cdylib"
|
||||
LIB_EXT = "dll"
|
||||
BUILD_ARCHS = "x64"
|
||||
APP_ENVIRONMENT = "production"
|
||||
|
||||
[env.development-linux-x86_64]
|
||||
@@ -133,6 +140,7 @@ LIB_EXT = "so"
|
||||
LINUX_ARCH = "x64"
|
||||
|
||||
[env.production-linux-x86_64]
|
||||
CARGO_PROFILE = "release"
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "linux"
|
||||
RUST_COMPILE_TARGET = "x86_64-unknown-linux-gnu"
|
||||
@@ -153,6 +161,7 @@ LINUX_ARCH = "arm64"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[env.production-linux-aarch64]
|
||||
CARGO_PROFILE = "release"
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "linux"
|
||||
RUST_COMPILE_TARGET = "aarch64-unknown-linux-gnu"
|
||||
@@ -179,6 +188,31 @@ RUST_COMPILE_TARGET = "aarch64-apple-ios"
|
||||
BUILD_ARCHS = "arm64"
|
||||
CRATE_TYPE = "staticlib"
|
||||
|
||||
[env.production-ios-arm64]
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "ios"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
RUST_COMPILE_TARGET = "aarch64-apple-ios"
|
||||
BUILD_ARCHS = "arm64"
|
||||
CRATE_TYPE = "staticlib"
|
||||
|
||||
[env.development-android]
|
||||
BUILD_FLAG = "debug"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Debug"
|
||||
LIB_EXT = "so"
|
||||
PRODUCT_EXT = "apk"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[env.production-android]
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
PRODUCT_EXT = "apk"
|
||||
LIB_EXT = "so"
|
||||
|
||||
[tasks.echo_env]
|
||||
script = ['''
|
||||
echo "-------- Env Parameters --------"
|
||||
@@ -191,23 +225,10 @@ script = ['''
|
||||
echo APP_ENVIRONMENT: ${APP_ENVIRONMENT}
|
||||
echo ${platforms}
|
||||
echo ${BUILD_ARCHS}
|
||||
echo ${BUILD_VERSION}
|
||||
''']
|
||||
script_runner = "@shell"
|
||||
|
||||
[env.development-android]
|
||||
BUILD_FLAG = "debug"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Debug"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[env.production-android]
|
||||
BUILD_FLAG = "release"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
|
||||
|
||||
[tasks.setup-crate-type]
|
||||
private = true
|
||||
script = [
|
||||
|
||||
@@ -69,8 +69,8 @@ windows/flutter/dart_ffi/
|
||||
**/.sandbox
|
||||
**/.vscode/
|
||||
|
||||
*.env
|
||||
*.env.*
|
||||
.env
|
||||
.env.*
|
||||
|
||||
coverage/
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
channel: stable
|
||||
revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
channel: unknown
|
||||
|
||||
project_type: app
|
||||
|
||||
@@ -13,11 +13,11 @@ project_type: app
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
- platform: windows
|
||||
create_revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
base_revision: 135454af32477f815a7525073027a3ff9eff1bfd
|
||||
create_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
base_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
- platform: android
|
||||
create_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
base_revision: 682aa387cfe4fbd71ccd5418b2c2a075729a1c66
|
||||
|
||||
# User provided section
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<h1 align="center" style="margin:0"> AppFlowy_Flutter</h1>
|
||||
<div align="center">
|
||||
<img src="https://img.shields.io/badge/Flutter-v3.10.1-blue"/>
|
||||
<img src="https://img.shields.io/badge/Rust-v1.65-orange"/>
|
||||
<img src="https://img.shields.io/badge/Flutter-v3.13.19-blue"/>
|
||||
<img src="https://img.shields.io/badge/Rust-v1.70-orange"/>
|
||||
</div>
|
||||
|
||||
> Documentation for Contributors
|
||||
@@ -13,11 +13,13 @@ This Repository contains the codebase for the frontend of the application, curre
|
||||
- Linux
|
||||
- macOS
|
||||
- Windows
|
||||
> We later expect to extend support to Android and iOS devices using Flutter.
|
||||
> We are actively working on support for Android & iOS!
|
||||
|
||||
_Additionally, we are working on a Web version built with Tauri!_
|
||||
|
||||
### Am I Eligible to Contribute?
|
||||
|
||||
Yes! You are eligible to contribute, check out the ways in which you can [contribute to AppFlowy](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/contributing-to-appflowy). Some of the ways in which you can contribute are:
|
||||
Yes! You are eligible to contribute, check out the ways in which you can [contribute to AppFlowy](https://docs.appflowy.io/docs/documentation/software-contributions/contributing-to-appflowy). Some of the ways in which you can contribute are:
|
||||
|
||||
- Non-Coding Contributions
|
||||
- Documentation
|
||||
@@ -26,27 +28,23 @@ Yes! You are eligible to contribute, check out the ways in which you can [contri
|
||||
- Improve Translations
|
||||
- Coding Contributions
|
||||
|
||||
To contribute to `AppFlowy_Flutter` codebase specifically (coding contribution) we suggest you to have basic knowledge of Flutter. In case you are new to Flutter, we may suggest you to learn the basics and then try to contribute, get started with Flutter [here](https://flutter.dev/docs/get-started/codelab).
|
||||
To contribute to `AppFlowy_Flutter` codebase specifically (coding contribution) we suggest you to have basic knowledge of Flutter. In case you are new to Flutter, we suggest you learn the basics, and then contribute afterwards. To get started with Flutter read [here](https://flutter.dev/docs/get-started/codelab).
|
||||
|
||||
### What OS Should I Use for Development?
|
||||
### What OS should I use for development?
|
||||
|
||||
We support all OS for Development i.e Linux, macOS and Windows. However, most of us promote macOS and Linux over Windows. We have detailed [docs](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/environment-setup) on How to Setup `AppFlowy_Flutter` in your local system in each OS.
|
||||
We support all OS for Development i.e. Linux, MacOS and Windows. However, most of us promote macOS and Linux over Windows. We have detailed [docs](https://docs.appflowy.io/docs/documentation/appflowy/from-source/environment-setup) on how to setup `AppFlowy_Flutter` on your local system respectively per operating system.
|
||||
|
||||
### Getting Started ❇
|
||||
|
||||
We have a detailed documentation, on how to [get started](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/contributing-to-appflowy) with the project, and make your first contribution. However, we do have some specific picks for you.
|
||||
We have detailed documentation on how to [get started](https://docs.appflowy.io/docs/documentation/software-contributions/contributing-to-appflowy) with the project, and make your first contribution. However, we do have some specific picks for you:
|
||||
|
||||
- [Code Architecture](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/frontend/frontend/codemap)
|
||||
- [Making Your First PR](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/submitting-code/submitting-your-first-pull-request)
|
||||
- [The Style Guide](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/submitting-code/style-guides)
|
||||
- [How to run/debug the application](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/launcher-and-tasks)
|
||||
- [Styleguide & Conventions](https://docs.appflowy.io/docs/documentation/software-contributions/conventions/naming-conventions)
|
||||
- [Making Your First PR](https://docs.appflowy.io/docs/documentation/software-contributions/submitting-code/submitting-your-first-pull-request)
|
||||
- [All AppFlowy Documentation](https://docs.appflowy.io/docs/documentation/appflowy) - Contribution guide, build and run, debugging, testing, localization, etc.
|
||||
|
||||
### Need Help?
|
||||
|
||||
- New to GitHub? Follow [these](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/software-contributions/submitting-code/setting-up-your-repositories) steps to get started
|
||||
- Stuck Somewhere? Join the [Discord](https://discord.gg/9Q2xaN37tV) Group and we are there to help you!
|
||||
|
||||
<!--
|
||||
## release check
|
||||
1. [entitlements](https://flutter.dev/desktop#setting-up-entitlements)
|
||||
2. [symbols stripped](https://flutter.dev/docs/development/platform-integration/c-interop) -->
|
||||
- New to GitHub? Follow [these](https://docs.appflowy.io/docs/documentation/software-contributions/submitting-code/setting-up-your-repositories) steps to get started
|
||||
- Stuck Somewhere? Join our [Discord](https://discord.gg/9Q2xaN37tV), we're there to help you!
|
||||
- Find out more about the [community initiatives](https://docs.appflowy.io/docs/appflowy/community).
|
||||
@@ -34,6 +34,13 @@ linter:
|
||||
- prefer_final_in_for_each
|
||||
- prefer_final_locals
|
||||
|
||||
- sized_box_for_whitespace
|
||||
- avoid_unnecessary_containers
|
||||
|
||||
- always_declare_return_types
|
||||
- unnecessary_raw_strings
|
||||
- use_decorated_box
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
||||
|
||||
@@ -11,3 +11,5 @@ GeneratedPluginRegistrant.java
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
||||
|
||||
.cxx
|
||||
@@ -25,8 +25,14 @@ apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
def keystoreProperties = new Properties()
|
||||
def keystorePropertiesFile = rootProject.file('key.properties')
|
||||
if (keystorePropertiesFile.exists()) {
|
||||
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
compileSdkVersion 34
|
||||
ndkVersion "24.0.8215888"
|
||||
|
||||
compileOptions {
|
||||
@@ -46,23 +52,41 @@ android {
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "io.appflowy.appflowy"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 31
|
||||
minSdkVersion 33
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
multiDexEnabled true
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_STL=c++_shared"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias keystoreProperties['keyAlias']
|
||||
keyPassword keystoreProperties['keyPassword']
|
||||
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
|
||||
storePassword keystoreProperties['storePassword']
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
|
||||
// use release instead when publishing the application to google play.
|
||||
// signingConfig signingConfigs.release
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
namespace 'io.appflowy.appflowy'
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path "src/main/CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flutter {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.appflowy.appflowy">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
||||
@@ -1,42 +1,55 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.appflowy.appflowy">
|
||||
<application
|
||||
android:label="appflowy_flutter"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:name="${applicationName}">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
</manifest>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application android:label="AppFlowy" android:icon="@mipmap/ic_launcher"
|
||||
android:name="${applicationName}">
|
||||
<activity android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
|
||||
<!--
|
||||
Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI.
|
||||
-->
|
||||
<meta-data android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
<!--
|
||||
Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame.
|
||||
-->
|
||||
<meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http" />
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="appflowy-flutter" />
|
||||
<!-- <data android:host="login-callback" /> -->
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!--
|
||||
Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
|
||||
-->
|
||||
<meta-data android:name="flutterEmbedding" android:value="2" />
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
|
||||
project(AppFlowy)
|
||||
|
||||
message(CONFIGURE_LOG "NDK PATH: ${ANDROID_NDK}")
|
||||
message(CONFIGURE_LOG "Copying libc++_shared.so")
|
||||
|
||||
# arm64-v8a
|
||||
file(COPY
|
||||
${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/arm64-v8a/libc++_shared.so
|
||||
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/arm64-v8a
|
||||
)
|
||||
|
||||
# x86_64
|
||||
file(COPY
|
||||
${ANDROID_NDK}/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++_shared.so
|
||||
DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/x86_64
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int64_t init_sdk(char *data);
|
||||
|
||||
void async_event(int64_t port, const uint8_t *input, uintptr_t len);
|
||||
|
||||
const uint8_t *sync_event(const uint8_t *input, uintptr_t len);
|
||||
|
||||
int32_t set_stream_port(int64_t port);
|
||||
|
||||
void link_me_please(void);
|
||||
|
||||
void backend_log(int64_t level, const char *data);
|
||||
|
||||
void set_env(const char *data);
|
||||
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 17 KiB |
@@ -1,5 +1,4 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.appflowy.appflowy">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Flutter needs it to communicate with the running application
|
||||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.10'
|
||||
ext.kotlin_version = '1.8.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,6 @@ subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
@@ -2,3 +2,4 @@ org.gradle.jvmargs=-Xmx1536M
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
org.gradle.caching=true
|
||||
android.suppressUnsupportedCompileSdk=33
|
||||
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
Before Width: | Height: | Size: 30 KiB |
@@ -0,0 +1 @@
|
||||
use_precompiled_binaries: true
|
||||
@@ -0,0 +1 @@
|
||||
APPFLOWY_CLOUD_URL=
|
||||
@@ -0,0 +1 @@
|
||||
extensions:
|
||||
@@ -4,6 +4,7 @@ import 'package:appflowy/workspace/presentation/settings/widgets/settings_appear
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
@@ -15,7 +16,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
@@ -47,7 +48,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
@@ -80,7 +81,8 @@ void main() {
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
|
||||
expect(find.textContaining(DefaultAppearanceSettings.kDefaultFontFamily),
|
||||
expect(
|
||||
find.textContaining(DefaultAppearanceSettings.kDefaultFontFamily),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('auth', () {
|
||||
testWidgets('sign in with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
tester.expectToSeeHomePage();
|
||||
});
|
||||
|
||||
testWidgets('sign out with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.tapButton(find.byType(SettingLogoutButton));
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.button_OK.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_OK.tr());
|
||||
|
||||
// Go to the sign in page again
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
testWidgets('sign in as annoymous', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapSignInAsGuest();
|
||||
|
||||
// should not see the sync setting page when sign in as annoymous
|
||||
await tester.openSettings();
|
||||
await tester.expectNoSettingsPage(SettingsPage.syncSetting);
|
||||
});
|
||||
|
||||
testWidgets('enable encryption', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.syncSetting);
|
||||
|
||||
// the switch should be off by default
|
||||
tester.assertEnableEncryptSwitchValue(false);
|
||||
await tester.toggleEnableEncrypt();
|
||||
|
||||
// the switch should be on after toggling
|
||||
tester.assertEnableEncryptSwitchValue(true);
|
||||
|
||||
// the switch can not be toggled back to off
|
||||
await tester.toggleEnableEncrypt();
|
||||
tester.assertEnableEncryptSwitchValue(true);
|
||||
});
|
||||
|
||||
testWidgets('enable sync', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.syncSetting);
|
||||
|
||||
// the switch should be on by default
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync();
|
||||
|
||||
// the switch should be off
|
||||
tester.assertEnableSyncSwitchValue(false);
|
||||
|
||||
// the switch should be on after toggling
|
||||
await tester.toggleEnableSync();
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/card/container/card_container.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
const defaultFirstCardName = 'Card 1';
|
||||
const defaultLastCardName = 'Card 3';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board add row test:', () {
|
||||
testWidgets('from header', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findFirstCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(FlowyText),
|
||||
);
|
||||
|
||||
FlowyText firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.text, defaultFirstCardName);
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svg == FlowySvgs.add_s,
|
||||
),
|
||||
)
|
||||
.at(1),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(RowCardContainer),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.text, newCardName);
|
||||
});
|
||||
|
||||
testWidgets('from footer', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findLastCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(FlowyText),
|
||||
);
|
||||
|
||||
FlowyText? lastCardText =
|
||||
tester.widgetList(findLastCard).last as FlowyText;
|
||||
expect(lastCardText.text, defaultLastCardName);
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(AppFlowyGroupFooter),
|
||||
matching: find.byType(FlowySvg),
|
||||
)
|
||||
.at(1),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(RowCardContainer),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
lastCardText = tester.widgetList(findLastCard).last as FlowyText;
|
||||
expect(lastCardText.text, newCardName);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/select_option_cell/extension.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_property.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board group test', () {
|
||||
testWidgets('move row to another group', (tester) async {
|
||||
const card1Name = 'Card 1';
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
final card1 = find.ancestor(
|
||||
of: find.findTextInFlowyText(card1Name),
|
||||
matching: find.byType(AppFlowyGroupCard),
|
||||
);
|
||||
final doingGroup = find.findTextInFlowyText('Doing');
|
||||
final doingGroupCenter = tester.getCenter(doingGroup);
|
||||
final card1Center = tester.getCenter(card1);
|
||||
|
||||
await tester.timedDrag(
|
||||
card1,
|
||||
doingGroupCenter.translate(-card1Center.dx, -card1Center.dy),
|
||||
const Duration(seconds: 1),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(card1);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final card1StatusFinder = find.descendant(
|
||||
of: find.byType(RowPropertyList),
|
||||
matching: find.descendant(
|
||||
of: find.byType(SelectOptionTag),
|
||||
matching: find.byType(FlowyText),
|
||||
),
|
||||
);
|
||||
expect(card1StatusFinder, findsNWidgets(1));
|
||||
final card1StatusText = tester.widget<FlowyText>(card1StatusFinder).text;
|
||||
expect(card1StatusText, 'Doing');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/widgets/board_hidden_groups.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board group options:', () {
|
||||
testWidgets('expand/collapse hidden groups', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final collapseFinder = find.byFlowySvg(FlowySvgs.pull_left_outlined_s);
|
||||
final expandFinder = find.byFlowySvg(FlowySvgs.hamburger_s_s);
|
||||
|
||||
// Is expanded by default
|
||||
expect(collapseFinder, findsOneWidget);
|
||||
expect(expandFinder, findsNothing);
|
||||
|
||||
// Collapse hidden groups
|
||||
await tester.tap(collapseFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Is collapsed
|
||||
expect(collapseFinder, findsNothing);
|
||||
expect(expandFinder, findsOneWidget);
|
||||
|
||||
// Expand hidden groups
|
||||
await tester.tap(expandFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Is expanded
|
||||
expect(collapseFinder, findsOneWidget);
|
||||
expect(expandFinder, findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('hide first group, and show it again', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
// Tap the options of the first group
|
||||
final optionsFinder = find
|
||||
.descendant(
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byFlowySvg(FlowySvgs.details_horizontal_s),
|
||||
)
|
||||
.first;
|
||||
|
||||
await tester.tap(optionsFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap the hide option
|
||||
await tester.tap(find.byFlowySvg(FlowySvgs.hide_s));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
int shownGroups =
|
||||
tester.widgetList(find.byType(BoardColumnHeader)).length;
|
||||
|
||||
// We still show Doing, Done, No Status
|
||||
expect(shownGroups, 3);
|
||||
|
||||
final hiddenCardFinder = find.byType(HiddenGroupCard);
|
||||
await tester.hoverOnWidget(hiddenCardFinder);
|
||||
await tester.tap(find.byFlowySvg(FlowySvgs.show_m));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
shownGroups = tester.widgetList(find.byType(BoardColumnHeader)).length;
|
||||
expect(shownGroups, 4);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('delete a group', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 4);
|
||||
|
||||
// tap group option button for the first group. Delete shouldn't show up
|
||||
await tester.tapButton(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byFlowySvg(FlowySvgs.details_horizontal_s),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
expect(find.byFlowySvg(FlowySvgs.delete_s), findsNothing);
|
||||
|
||||
// dismiss the popup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// tap group option button for the first group. Delete should show up
|
||||
await tester.tapButton(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byFlowySvg(FlowySvgs.details_horizontal_s),
|
||||
)
|
||||
.at(1),
|
||||
);
|
||||
expect(find.byFlowySvg(FlowySvgs.delete_s), findsOneWidget);
|
||||
|
||||
// Tap the delete button and confirm
|
||||
await tester.tapButton(find.byFlowySvg(FlowySvgs.delete_s));
|
||||
await tester.tapDialogOkButton();
|
||||
|
||||
// Expect number of groups to decrease by one
|
||||
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 3);
|
||||
});
|
||||
}
|
||||
|
||||
extension FlowySvgFinder on CommonFinders {
|
||||
Finder byFlowySvg(FlowySvgData svg) => _FlowySvgFinder(svg);
|
||||
}
|
||||
|
||||
class _FlowySvgFinder extends MatchFinder {
|
||||
_FlowySvgFinder(this.svg);
|
||||
|
||||
final FlowySvgData svg;
|
||||
|
||||
@override
|
||||
String get description => 'flowy_svg "$svg"';
|
||||
|
||||
@override
|
||||
bool matches(Element candidate) {
|
||||
final Widget widget = candidate.widget;
|
||||
return widget is FlowySvg && widget.svg == svg;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/card/card.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/card/card.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -15,7 +18,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
const name = 'Card 1';
|
||||
final card1 = find.findTextInFlowyText(name);
|
||||
await tester.hoverOnWidget(
|
||||
@@ -33,7 +36,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
const name = 'Card 1';
|
||||
final card1 = find.findTextInFlowyText(name);
|
||||
await tester.hoverOnWidget(
|
||||
@@ -46,5 +49,56 @@ void main() {
|
||||
await tester.tapButtonWithName(LocaleKeys.button_duplicate.tr());
|
||||
expect(find.textContaining(name, findRichText: true), findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('add new group', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
// assert number of groups
|
||||
tester.assertNumberOfGroups(4);
|
||||
|
||||
// scroll the board horizontally to ensure add new group button appears
|
||||
await tester.scrollBoardToEnd();
|
||||
|
||||
// assert and click on add new group button
|
||||
tester.assertNewGroupTextField(false);
|
||||
await tester.tapNewGroupButton();
|
||||
tester.assertNewGroupTextField(true);
|
||||
|
||||
// enter new group name and submit
|
||||
await tester.enterNewGroupName('needs design', submit: true);
|
||||
|
||||
// assert number of groups has increased
|
||||
tester.assertNumberOfGroups(5);
|
||||
|
||||
// assert text field has disappeared
|
||||
await tester.scrollBoardToEnd();
|
||||
tester.assertNewGroupTextField(false);
|
||||
|
||||
// click on add new group button
|
||||
await tester.tapNewGroupButton();
|
||||
tester.assertNewGroupTextField(true);
|
||||
|
||||
// type some things
|
||||
await tester.enterNewGroupName('needs planning', submit: false);
|
||||
|
||||
// click on clear button and assert empty contents
|
||||
await tester.clearNewGroupTextField();
|
||||
|
||||
// press escape to cancel
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
await tester.pumpAndSettle();
|
||||
tester.assertNewGroupTextField(false);
|
||||
|
||||
// click on add new group button
|
||||
await tester.tapNewGroupButton();
|
||||
tester.assertNewGroupTextField(true);
|
||||
|
||||
// press elsewhere to cancel
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
tester.assertNewGroupTextField(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'board_row_test.dart' as board_row_test;
|
||||
import 'board_add_row_test.dart' as board_add_row_test;
|
||||
import 'board_group_test.dart' as board_group_test;
|
||||
|
||||
void startTesting() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Board integration tests
|
||||
board_row_test.main();
|
||||
board_add_row_test.main();
|
||||
board_group_test.main();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/dir.dart';
|
||||
import '../util/mock/mock_file_picker.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('appflowy cloud', () {
|
||||
testWidgets('anon user and then sign in', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
|
||||
await tester.tapGoButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// reanme the name of the anon user
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
final userNameFinder = find.descendant(
|
||||
of: find.byType(SettingsUserView),
|
||||
matching: find.byType(UserNameInput),
|
||||
);
|
||||
await tester.enterText(userNameFinder, 'local_user');
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// sign up with Google
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// sign out
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.logout();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// tap the continue as anonymous button
|
||||
await tester
|
||||
.tapButton(find.text(LocaleKeys.signIn_loginStartWithAnonymous.tr()));
|
||||
await tester.expectToSeeHomePage();
|
||||
|
||||
// New anon user name
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
final userNameInput = tester.widget(userNameFinder) as UserNameInput;
|
||||
expect(userNameInput.name, 'Me');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/mock/mock_file_picker.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('appflowy cloud auth', () {
|
||||
testWidgets('sign in', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
});
|
||||
|
||||
testWidgets('sign out', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.tapButton(find.byType(SettingLogoutButton));
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.button_ok.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
|
||||
// Go to the sign in page again
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
testWidgets('sign in as annoymous', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapSignInAsGuest();
|
||||
|
||||
// should not see the sync setting page when sign in as annoymous
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
testWidgets('enable sync', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
|
||||
await tester.tapGoogleLoginInButton();
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// the switch should be on by default
|
||||
tester.assertAppFlowyCloudEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync(AppFlowyCloudEnableSync);
|
||||
|
||||
// the switch should be off
|
||||
tester.assertAppFlowyCloudEnableSyncSwitchValue(false);
|
||||
|
||||
// the switch should be on after toggling
|
||||
await tester.toggleEnableSync(AppFlowyCloudEnableSync);
|
||||
tester.assertAppFlowyCloudEnableSyncSwitchValue(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import 'empty_test.dart' as preset_af_cloud_env_test;
|
||||
import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test;
|
||||
// import 'document_sync_test.dart' as document_sync_test;
|
||||
import 'user_setting_sync_test.dart' as user_sync_test;
|
||||
import 'anon_user_continue_test.dart' as anon_user_continue_test;
|
||||
|
||||
Future<void> main() async {
|
||||
preset_af_cloud_env_test.main();
|
||||
|
||||
appflowy_cloud_auth_test.main();
|
||||
|
||||
// document_sync_test.main();
|
||||
|
||||
user_sync_test.main();
|
||||
|
||||
anon_user_continue_test.main();
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/dir.dart';
|
||||
import '../util/mock/mock_file_picker.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
final email = '${uuid()}@appflowy.io';
|
||||
const inputContent = 'Hello world, this is a test document';
|
||||
|
||||
// The test will create a new document called Sample, and sync it to the server.
|
||||
// Then the test will logout the user, and login with the same user. The data will
|
||||
// be synced from the server.
|
||||
group('appflowy cloud document', () {
|
||||
testWidgets('sync local docuemnt to server', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// create a new document called Sample
|
||||
await tester.createNewPage(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// focus on the editor
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.ime.insertText(inputContent);
|
||||
expect(find.text(inputContent, findRichText: true), findsOneWidget);
|
||||
|
||||
// TODO(nathan): remove the await
|
||||
// 6 seconds for data sync
|
||||
await tester.waitForSeconds(6);
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.logout();
|
||||
});
|
||||
|
||||
testWidgets('sync doc from server', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePage();
|
||||
|
||||
// the latest document will be opened, so the content must be the inputContent
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(inputContent, findRichText: true), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
// This test is meaningless, just for preventing the CI from failing.
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Empty', () {
|
||||
testWidgets('set appflowy cloud', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('supabase auth', () {
|
||||
testWidgets('sign in with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
});
|
||||
|
||||
testWidgets('sign out with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.tapButton(find.byType(SettingLogoutButton));
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.button_ok.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
|
||||
// Go to the sign in page again
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
testWidgets('sign in as annoymous', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapSignInAsGuest();
|
||||
|
||||
// should not see the sync setting page when sign in as annoymous
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
// testWidgets('enable encryption', (tester) async {
|
||||
// await tester.initializeAppFlowy(cloudType: CloudType.supabase);
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
|
||||
// // Open the setting page and sign out
|
||||
// await tester.openSettings();
|
||||
// await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// // the switch should be off by default
|
||||
// tester.assertEnableEncryptSwitchValue(false);
|
||||
// await tester.toggleEnableEncrypt();
|
||||
|
||||
// // the switch should be on after toggling
|
||||
// tester.assertEnableEncryptSwitchValue(true);
|
||||
|
||||
// // the switch can not be toggled back to off
|
||||
// await tester.toggleEnableEncrypt();
|
||||
// tester.assertEnableEncryptSwitchValue(true);
|
||||
// });
|
||||
|
||||
testWidgets('enable sync', (tester) async {
|
||||
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// the switch should be on by default
|
||||
tester.assertSupabaseEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync(SupabaseEnableSync);
|
||||
|
||||
// the switch should be off
|
||||
tester.assertSupabaseEnableSyncSwitchValue(false);
|
||||
|
||||
// the switch should be on after toggling
|
||||
await tester.toggleEnableSync(SupabaseEnableSync);
|
||||
tester.assertSupabaseEnableSyncSwitchValue(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/dir.dart';
|
||||
import '../util/emoji.dart';
|
||||
import '../util/mock/mock_file_picker.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
final email = '${uuid()}@appflowy.io';
|
||||
const name = 'nathan';
|
||||
|
||||
group('appflowy cloud setting', () {
|
||||
testWidgets('sync user name and icon to server', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
final userAvatarFinder = find.descendant(
|
||||
of: find.byType(SettingsUserView),
|
||||
matching: find.byType(UserAvatar),
|
||||
);
|
||||
|
||||
// Open icon picker dialog and select emoji
|
||||
await tester.tap(userAvatarFinder);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tapEmoji('😁');
|
||||
await tester.pumpAndSettle();
|
||||
final UserAvatar userAvatar =
|
||||
tester.widget(userAvatarFinder) as UserAvatar;
|
||||
expect(userAvatar.iconUrl, '😁');
|
||||
|
||||
// enter user name
|
||||
final userNameFinder = find.descendant(
|
||||
of: find.byType(SettingsUserView),
|
||||
matching: find.byType(UserNameInput),
|
||||
);
|
||||
await tester.enterText(userNameFinder, name);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tapEscButton();
|
||||
|
||||
// wait 2 seconds for the sync to finish
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('get user icon and name from server', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
|
||||
// verify icon
|
||||
final userAvatarFinder = find.descendant(
|
||||
of: find.byType(SettingsUserView),
|
||||
matching: find.byType(UserAvatar),
|
||||
);
|
||||
final UserAvatar userAvatar = tester.widget(userAvatarFinder) as UserAvatar;
|
||||
expect(userAvatar.iconUrl, '😁');
|
||||
|
||||
// verify name
|
||||
final userNameFinder = find.descendant(
|
||||
of: find.byType(SettingsUserView),
|
||||
matching: find.byType(UserNameInput),
|
||||
);
|
||||
final UserNameInput userNameInput =
|
||||
tester.widget(userNameFinder) as UserNameInput;
|
||||
expect(userNameInput.name, name);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('calendar', () {
|
||||
testWidgets('update calendar layout', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
|
||||
// open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapDatabaseLayoutButton();
|
||||
await tester.selectDatabaseLayoutType(DatabaseLayoutPB.Board);
|
||||
await tester.assertCurrentDatabaseLayoutType(DatabaseLayoutPB.Board);
|
||||
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapDatabaseLayoutButton();
|
||||
await tester.selectDatabaseLayoutType(DatabaseLayoutPB.Grid);
|
||||
await tester.assertCurrentDatabaseLayoutType(DatabaseLayoutPB.Grid);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('calendar start from day setting', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create calendar view
|
||||
const name = 'calendar';
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
|
||||
// Open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapCalendarLayoutSettingButton();
|
||||
|
||||
// select the first day of week is Monday
|
||||
await tester.tapFirstDayOfWeek();
|
||||
await tester.tapFirstDayOfWeekStartFromMonday();
|
||||
|
||||
// Open the other page and open the new calendar page again
|
||||
await tester.openPage(gettingStarted);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
await tester.openPage(name, layout: ViewLayoutPB.Calendar);
|
||||
|
||||
// Open setting again and check the start from Monday is selected
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapCalendarLayoutSettingButton();
|
||||
await tester.tapFirstDayOfWeek();
|
||||
tester.assertFirstDayOfWeekStartFromMonday();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('creating and editing calendar events', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create the calendar view
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
|
||||
// Scroll until today's date cell is visible
|
||||
await tester.scrollToToday();
|
||||
|
||||
// Hover over today's calendar cell
|
||||
await tester.hoverOnTodayCalendarCell(
|
||||
// Tap on create new event button
|
||||
onHover: () async => await tester.tapAddCalendarEventButton(),
|
||||
);
|
||||
|
||||
// Make sure that the event editor popup is shown
|
||||
tester.assertEventEditorOpen();
|
||||
|
||||
tester.assertNumberOfEventsInCalendar(1);
|
||||
|
||||
// Dismiss the event editor popup
|
||||
await tester.dismissEventEditor();
|
||||
|
||||
// Double click on today's calendar cell to create a new event
|
||||
await tester.doubleClickCalendarCell(DateTime.now());
|
||||
|
||||
// Make sure that the event is inserted in the cell
|
||||
tester.assertNumberOfEventsInCalendar(2);
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
tester.assertEventEditorOpen();
|
||||
|
||||
// Change the title of the event
|
||||
await tester.editEventTitle('hello world');
|
||||
await tester.dismissEventEditor();
|
||||
|
||||
// Make sure that the event is edited
|
||||
tester.assertNumberOfEventsInCalendar(1, title: 'hello world');
|
||||
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
tester.assertEventEditorOpen();
|
||||
|
||||
// Click on the open icon
|
||||
await tester.openEventToRowDetailPage();
|
||||
tester.assertRowDetailPageOpened();
|
||||
|
||||
// Duplicate the event
|
||||
await tester.tapRowDetailPageRowActionButton();
|
||||
await tester.tapRowDetailPageDuplicateRowButton();
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
// Check that there are 2 events
|
||||
tester.assertNumberOfEventsInCalendar(2, title: 'hello world');
|
||||
tester.assertNumberOfEventsOnSpecificDay(3, DateTime.now());
|
||||
|
||||
// Delete an event
|
||||
await tester.openCalendarEvent(index: 1);
|
||||
await tester.deleteEventFromEventEditor();
|
||||
|
||||
// Check that there is 1 event
|
||||
tester.assertNumberOfEventsInCalendar(1, title: 'hello world');
|
||||
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
||||
|
||||
// Delete event from row detail page
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
await tester.openEventToRowDetailPage();
|
||||
tester.assertRowDetailPageOpened();
|
||||
|
||||
await tester.tapRowDetailPageRowActionButton();
|
||||
await tester.tapRowDetailPageDeleteRowButton();
|
||||
|
||||
// Check that there is 0 event
|
||||
tester.assertNumberOfEventsInCalendar(0, title: 'hello world');
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, DateTime.now());
|
||||
});
|
||||
|
||||
testWidgets('rescheduling events', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create the calendar view
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
|
||||
// Create a new event on the first of this month
|
||||
final today = DateTime.now();
|
||||
final firstOfThisMonth = DateTime(today.year, today.month, 1);
|
||||
await tester.doubleClickCalendarCell(firstOfThisMonth);
|
||||
await tester.dismissEventEditor();
|
||||
|
||||
// Drag and drop the event onto the next week, same day
|
||||
await tester.dragDropRescheduleCalendarEvent();
|
||||
|
||||
// Make sure that the event has been rescheduled to the new date
|
||||
final sameDayNextWeek = firstOfThisMonth.add(const Duration(days: 7));
|
||||
tester.assertNumberOfEventsInCalendar(1);
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, sameDayNextWeek);
|
||||
|
||||
// Delete the event
|
||||
await tester.openCalendarEvent(index: 0, date: sameDayNextWeek);
|
||||
await tester.deleteEventFromEventEditor();
|
||||
|
||||
// Create another event on the 5th of this month
|
||||
final fifthOfThisMonth = DateTime(today.year, today.month, 5);
|
||||
await tester.doubleClickCalendarCell(fifthOfThisMonth);
|
||||
await tester.dismissEventEditor();
|
||||
|
||||
// Make sure that the event is on the 4t
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, fifthOfThisMonth);
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 0, date: fifthOfThisMonth);
|
||||
|
||||
// Open the date editor of the event
|
||||
await tester.tapDateCellInRowDetailPage();
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Edit the event's date
|
||||
final newDate = fifthOfThisMonth.add(const Duration(days: 1));
|
||||
await tester.selectDay(content: newDate.day);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Dismiss the event editor
|
||||
await tester.dismissEventEditor();
|
||||
|
||||
// Make sure that the event is edited
|
||||
tester.assertNumberOfEventsInCalendar(1);
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, newDate);
|
||||
|
||||
// Click on the unscheduled events button
|
||||
await tester.openUnscheduledEventsPopup();
|
||||
|
||||
// Assert that nothing shows up
|
||||
tester.findUnscheduledPopup(findsNothing, 0);
|
||||
|
||||
// Click on the event in the calendar
|
||||
await tester.openCalendarEvent(index: 0, date: newDate);
|
||||
|
||||
// Open the date editor of the event
|
||||
await tester.tapDateCellInRowDetailPage();
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Clear the date of the event
|
||||
await tester.clearDate();
|
||||
|
||||
// Dismiss the event editor
|
||||
await tester.dismissEventEditor();
|
||||
tester.assertNumberOfEventsInCalendar(0);
|
||||
|
||||
// Click on the unscheduled events button
|
||||
await tester.openUnscheduledEventsPopup();
|
||||
|
||||
// Assert that a popup appears and 1 unscheduled event
|
||||
tester.findUnscheduledPopup(findsOneWidget, 1);
|
||||
|
||||
// Click on the unscheduled event
|
||||
await tester.clickUnscheduledEvent();
|
||||
|
||||
tester.assertRowDetailPageOpened();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('edit grid cell:', () {
|
||||
testWidgets('text', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
input: 'hello world',
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'hello world',
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
// Make sure the text cells are filled with the right content when there are
|
||||
// multiple text cell
|
||||
testWidgets('multiple text cells', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'my grid',
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
await tester.createField(FieldType.RichText, 'description');
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
input: 'hello',
|
||||
);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
input: 'world',
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'hello',
|
||||
cellIndex: 0,
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'world',
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('number', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Number;
|
||||
|
||||
// Create a number field
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
input: '-1',
|
||||
);
|
||||
// edit the next cell to force the previous cell at row 0 to lose focus
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: fieldType,
|
||||
input: '0.2',
|
||||
);
|
||||
// -1 -> -1
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
content: '-1',
|
||||
);
|
||||
|
||||
// edit the next cell to force the previous cell at row 1 to lose focus
|
||||
await tester.editCell(
|
||||
rowIndex: 2,
|
||||
fieldType: fieldType,
|
||||
input: '.1',
|
||||
);
|
||||
// 0.2 -> 0.2
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 1,
|
||||
fieldType: fieldType,
|
||||
content: '0.2',
|
||||
);
|
||||
|
||||
// edit the next cell to force the previous cell at row 2 to lose focus
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
input: '',
|
||||
);
|
||||
// .1 -> 0.1
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 2,
|
||||
fieldType: fieldType,
|
||||
content: '0.1',
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('checkbox', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.assertCheckboxCell(rowIndex: 0, isSelected: false);
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 0);
|
||||
await tester.assertCheckboxCell(rowIndex: 0, isSelected: true);
|
||||
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 1);
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 2);
|
||||
await tester.assertCheckboxCell(rowIndex: 1, isSelected: true);
|
||||
await tester.assertCheckboxCell(rowIndex: 2, isSelected: true);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('created time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.CreatedTime;
|
||||
// Create a create time field
|
||||
// The create time field is not editable
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
|
||||
await tester.findDateEditor(findsNothing);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('last modified time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.LastEditedTime;
|
||||
// Create a last time field
|
||||
// The last time field is not editable
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
|
||||
await tester.findDateEditor(findsNothing);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('date time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.DateTime;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
// Tap the cell to invoke the field editor
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Toggle include time
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
// Dismiss the cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Turn off include time
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
// Select a date
|
||||
final today = DateTime.now();
|
||||
await tester.selectDay(content: today.day);
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
content: DateFormat('MMM dd, y').format(today),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Toggle include time
|
||||
final now = DateTime.now();
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
content: DateFormat('MMM dd, y HH:mm').format(now),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Change date format
|
||||
await tester.changeDateFormat();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
content: DateFormat('dd/MM/y HH:mm').format(now),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Change time format
|
||||
await tester.changeTimeFormat();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
content: DateFormat('dd/MM/y hh:mm a').format(now),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Clear the date and time
|
||||
await tester.clearDate();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
content: '',
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('single select', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
const fieldType = FieldType.SingleSelect;
|
||||
|
||||
// When create a grid, it will create a single select field by default
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Tap the cell to invoke the selection option editor
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Create a new select option
|
||||
await tester.createOption(name: 'tag 1');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Make sure the option is created and displayed in the cell
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 1',
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Create another select option
|
||||
await tester.createOption(name: 'tag 2');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 2',
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsOneWidget,
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// switch to first option
|
||||
await tester.selectOption(name: 'tag 1');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 1',
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsOneWidget,
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Deselect the currently-selected option
|
||||
await tester.selectOption(name: 'tag 1');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNothing,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('multi select', (tester) async {
|
||||
final tags = [
|
||||
'tag 1',
|
||||
'tag 2',
|
||||
'tag 3',
|
||||
'tag 4',
|
||||
];
|
||||
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.MultiSelect;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
// Tap the cell to invoke the selection option editor
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Create a new select option
|
||||
await tester.createOption(name: tags.first);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Make sure the option is created and displayed in the cell
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags.first,
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Create some other select options
|
||||
await tester.createOption(name: tags[1]);
|
||||
await tester.createOption(name: tags[2]);
|
||||
await tester.createOption(name: tags[3]);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
for (final tag in tags) {
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tag,
|
||||
);
|
||||
}
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNWidgets(4),
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Deselect all options
|
||||
for (final tag in tags) {
|
||||
await tester.selectOption(name: tag);
|
||||
}
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNothing,
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Select some options
|
||||
await tester.selectOption(name: tags[1]);
|
||||
await tester.selectOption(name: tags[3]);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags[1],
|
||||
);
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags[3],
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNWidgets(2),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('checklist', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Checklist;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
// assert that there is no progress bar in the grid
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
|
||||
// tap on the first checklist cell
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// assert that the checklist editor is shown
|
||||
tester.assertChecklistEditorVisible(visible: true);
|
||||
|
||||
// create a new task with enter
|
||||
await tester.createNewChecklistTask(name: "task 0", enter: true);
|
||||
|
||||
// assert that the task is displayed
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 0,
|
||||
name: "task 0",
|
||||
isChecked: false,
|
||||
);
|
||||
|
||||
// update the task's name
|
||||
await tester.renameChecklistTask(index: 0, name: "task 1");
|
||||
|
||||
// assert that the task's name is updated
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 0,
|
||||
name: "task 1",
|
||||
isChecked: false,
|
||||
);
|
||||
|
||||
// dismiss new task editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// dismiss checklist cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// assert that progress bar is shown in grid at 0%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 0);
|
||||
|
||||
// start editing the first checklist cell again
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// create another task with the create button
|
||||
await tester.createNewChecklistTask(name: "task 2", button: true);
|
||||
|
||||
// assert that the task was inserted
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 1,
|
||||
name: "task 2",
|
||||
isChecked: false,
|
||||
);
|
||||
|
||||
// mark it as complete
|
||||
await tester.checkChecklistTask(index: 1);
|
||||
|
||||
// assert that the task was checked in the editor
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 1,
|
||||
name: "task 2",
|
||||
isChecked: true,
|
||||
);
|
||||
|
||||
// dismiss checklist editor
|
||||
await tester.dismissCellEditor();
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// assert that progressbar is shown in grid at 50%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 0.5);
|
||||
|
||||
// re-open the cell editor
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// hover over first task and delete it
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
|
||||
// dismiss cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// assert that progressbar is shown in grid at 100%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 1);
|
||||
|
||||
// re-open the cell edior
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// delete the remaining task
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
|
||||
// dismiss the cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// check that the progress bar is not viisble
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('database field settings', () {
|
||||
testWidgets('field visibility', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField('New field 1');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// hide the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapHidePropertyButton();
|
||||
await tester.noFieldWithName('New field 1');
|
||||
|
||||
// go back to inline database view, expect field to be shown
|
||||
await tester.tapTabBarLinkedViewByViewName('Untitled');
|
||||
await tester.findFieldWithName('New field 1');
|
||||
|
||||
// go back to linked database view, expect field to be hidden
|
||||
await tester.tapTabBarLinkedViewByViewName('Grid');
|
||||
await tester.noFieldWithName('New field 1');
|
||||
|
||||
// use the settings button to show the field
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapViewPropertiesButton();
|
||||
await tester.tapViewTogglePropertyVisibilityButtonByName('New field 1');
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.findFieldWithName('New field 1');
|
||||
|
||||
// open first row in popup then hide the field
|
||||
await tester.openFirstRowDetailPage();
|
||||
await tester.tapGridFieldWithNameInRowDetailPage('New field 1');
|
||||
await tester.tapHidePropertyButtonInFieldEditor();
|
||||
await tester.dismissRowDetailPage();
|
||||
await tester.noFieldWithName('New field 1');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/select_option.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/select/select_option.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid page', () {
|
||||
group('grid field editor:', () {
|
||||
testWidgets('rename existing field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Name');
|
||||
@@ -34,7 +34,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
@@ -56,7 +56,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checklist, 'checklist');
|
||||
@@ -70,7 +70,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checkbox, 'New field 1');
|
||||
@@ -90,7 +90,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
@@ -98,7 +98,7 @@ void main() {
|
||||
await tester.renameField('New field 1');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Delete the field
|
||||
// duplicate the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapDuplicatePropertyButton();
|
||||
|
||||
@@ -106,31 +106,34 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('hide field', (tester) async {
|
||||
testWidgets('insert field on either side of a field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField('New field 1');
|
||||
|
||||
// insert new field to the right
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapInsertFieldButton(left: false, name: 'Right');
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.findFieldWithName('Right');
|
||||
|
||||
// Delete the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapHidePropertyButton();
|
||||
// insert new field to the right
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapInsertFieldButton(left: true, name: "Left");
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.findFieldWithName('Left');
|
||||
|
||||
await tester.noFieldWithName('New field 1');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create checklist field ', (tester) async {
|
||||
testWidgets('create checklist field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
@@ -150,7 +153,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
for (final fieldType in [
|
||||
FieldType.Checklist,
|
||||
@@ -182,7 +185,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/text.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -0,0 +1,342 @@
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_banner.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/emoji.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid', () {
|
||||
testWidgets('row details page opens', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Make sure that the row page is opened
|
||||
tester.assertRowDetailPageOpened();
|
||||
});
|
||||
|
||||
testWidgets('insert emoji in the row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
await tester.hoverRowBanner();
|
||||
|
||||
await tester.openEmojiPicker();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// After select the emoji, the EmojiButton will show up
|
||||
await tester.tapButton(find.byType(EmojiButton));
|
||||
});
|
||||
|
||||
testWidgets('update emoji in the row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
await tester.hoverRowBanner();
|
||||
await tester.openEmojiPicker();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Update existing selected emoji
|
||||
await tester.tapButton(find.byType(EmojiButton));
|
||||
await tester.tapEmoji('😅');
|
||||
|
||||
// The emoji already displayed in the row banner
|
||||
final emojiText = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == '😅',
|
||||
);
|
||||
|
||||
// The number of emoji should be two. One in the row displayed in the grid
|
||||
// one in the row detail page.
|
||||
expect(emojiText, findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('remove emoji in the row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
await tester.hoverRowBanner();
|
||||
await tester.openEmojiPicker();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Remove the emoji
|
||||
await tester.tapButton(find.byType(RemoveEmojiButton));
|
||||
final emojiText = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == '😀',
|
||||
);
|
||||
expect(emojiText, findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('create list of fields in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
for (final fieldType in [
|
||||
FieldType.Checklist,
|
||||
FieldType.DateTime,
|
||||
FieldType.Number,
|
||||
FieldType.URL,
|
||||
FieldType.MultiSelect,
|
||||
FieldType.LastEditedTime,
|
||||
FieldType.CreatedTime,
|
||||
FieldType.Checkbox,
|
||||
]) {
|
||||
await tester.tapRowDetailPageCreatePropertyButton();
|
||||
await tester.renameField(fieldType.name);
|
||||
|
||||
// Open the type option menu
|
||||
await tester.tapTypeOptionButton();
|
||||
|
||||
await tester.selectFieldType(fieldType);
|
||||
|
||||
// After update the field type, the cells should be updated
|
||||
await tester.findCellByFieldType(fieldType);
|
||||
await tester.scrollRowDetailByOffset(const Offset(0, -50));
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('change order of fields and cells', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Assert that the first field in the row details page is the select
|
||||
// option type
|
||||
tester.assertFirstFieldInRowDetailByType(FieldType.SingleSelect);
|
||||
|
||||
// Reorder first field in list
|
||||
final gesture = await tester.hoverOnFieldInRowDetail(index: 0);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.reorderFieldInRowDetail(offset: 30);
|
||||
|
||||
// Orders changed, now the checkbox is first
|
||||
tester.assertFirstFieldInRowDetailByType(FieldType.Checkbox);
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Reorder second field in list
|
||||
await tester.hoverOnFieldInRowDetail(index: 1);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.reorderFieldInRowDetail(offset: -30);
|
||||
|
||||
// First field is now back to select option
|
||||
tester.assertFirstFieldInRowDetailByType(FieldType.SingleSelect);
|
||||
});
|
||||
|
||||
testWidgets('hide and show hidden fields', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Assert that the show hidden fields button isn't visible
|
||||
tester.assertToggleShowHiddenFieldsVisibility(false);
|
||||
|
||||
// Hide the first field in the field list
|
||||
await tester.tapGridFieldWithNameInRowDetailPage("Type");
|
||||
await tester.tapHidePropertyButtonInFieldEditor();
|
||||
|
||||
// Assert that the field is now hidden
|
||||
tester.noFieldWithName("Type");
|
||||
|
||||
// Assert that the show hidden fields button appears
|
||||
tester.assertToggleShowHiddenFieldsVisibility(true);
|
||||
|
||||
// Click on the show hidden fields button
|
||||
await tester.toggleShowHiddenFields();
|
||||
|
||||
// Assert that the hidden field is shown again and that the show
|
||||
// hidden fields button is still present
|
||||
tester.findFieldWithName("Type");
|
||||
tester.assertToggleShowHiddenFieldsVisibility(true);
|
||||
|
||||
// Click hide hidden fields
|
||||
await tester.toggleShowHiddenFields();
|
||||
|
||||
// Assert that the hidden field has vanished
|
||||
tester.noFieldWithName("Type");
|
||||
|
||||
// Click show hidden fields
|
||||
await tester.toggleShowHiddenFields();
|
||||
|
||||
// delete the hidden field
|
||||
await tester.tapGridFieldWithNameInRowDetailPage("Type");
|
||||
await tester.tapDeletePropertyInFieldEditor();
|
||||
|
||||
// Assert that the that the show hidden fields button is gone
|
||||
tester.assertToggleShowHiddenFieldsVisibility(false);
|
||||
});
|
||||
|
||||
testWidgets('check document exists in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Each row detail page should have a document
|
||||
await tester.assertDocumentExistInRowDetailPage();
|
||||
});
|
||||
|
||||
testWidgets('update the contents of the document and re-open it',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Wait for the document to be loaded
|
||||
await tester.wait(500);
|
||||
|
||||
// Focus on the editor
|
||||
final textBlock = find.byType(ParagraphBlockComponentWidget);
|
||||
await tester.tapAt(tester.getCenter(textBlock));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Input some text
|
||||
const inputText = 'Hello World';
|
||||
await tester.ime.insertText(inputText);
|
||||
expect(
|
||||
find.textContaining(inputText, findRichText: true),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// Tap outside to dismiss the field
|
||||
await tester.tapAt(Offset.zero);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Re-open the document
|
||||
await tester.openFirstRowDetailPage();
|
||||
expect(
|
||||
find.textContaining(inputText, findRichText: true),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'check if the title wraps properly when a long text is inserted',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Wait for the document to be loaded
|
||||
await tester.wait(500);
|
||||
|
||||
// Focus on the editor
|
||||
final textField = find
|
||||
.descendant(
|
||||
of: find.byType(SimpleDialog),
|
||||
matching: find.byType(TextField),
|
||||
)
|
||||
.first;
|
||||
|
||||
// Input a long text
|
||||
await tester.enterText(textField, 'Long text' * 25);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap outside to dismiss the field
|
||||
await tester.tapAt(Offset.zero);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Check if there is any overflow in the widget tree
|
||||
expect(tester.takeException(), isNull);
|
||||
|
||||
// Re-open the document
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Check again if there is any overflow in the widget tree
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('delete row in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
await tester.tapRowDetailPageRowActionButton();
|
||||
await tester.tapRowDetailPageDeleteRowButton();
|
||||
await tester.tapEscButton();
|
||||
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
});
|
||||
|
||||
testWidgets('duplicate row in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
await tester.tapRowDetailPageRowActionButton();
|
||||
await tester.tapRowDetailPageDuplicateRowButton();
|
||||
await tester.tapEscButton();
|
||||
|
||||
await tester.assertNumberOfRowsInGridPage(4);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -13,7 +13,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateRowButtonInGrid();
|
||||
|
||||
// The initial number of rows is 3
|
||||
@@ -25,7 +25,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
|
||||
@@ -41,7 +41,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
|
||||
// Open the row menu and then click the delete
|
||||
@@ -59,7 +59,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.assertRowCountInGridPage(3);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -14,7 +14,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
@@ -31,7 +31,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
@@ -2,7 +2,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -34,7 +34,7 @@ void main() {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
];
|
||||
for (final (index, content) in checkboxCells.indexed) {
|
||||
await tester.assertCheckboxCell(
|
||||
@@ -54,7 +54,7 @@ void main() {
|
||||
'10',
|
||||
'11',
|
||||
'12',
|
||||
''
|
||||
'',
|
||||
];
|
||||
for (final (index, content) in numberCells.indexed) {
|
||||
await tester.assertCellContent(
|
||||
@@ -122,17 +122,17 @@ void main() {
|
||||
}
|
||||
|
||||
// check the checklist cell
|
||||
final List<double> checklistCells = [
|
||||
0.6,
|
||||
0.3,
|
||||
final List<double?> checklistCells = [
|
||||
0.67,
|
||||
0.33,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
];
|
||||
for (final (index, percent) in checklistCells.indexed) {
|
||||
await tester.assertChecklistCellInGrid(
|
||||
@@ -152,12 +152,11 @@ void main() {
|
||||
'Jun 16, 2023',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
'',
|
||||
];
|
||||
for (final (index, content) in dateCells.indexed) {
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: content,
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import '../util/database_test_op.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -0,0 +1,77 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('database', () {
|
||||
testWidgets('create linked view', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// Create grid view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Grid);
|
||||
|
||||
// Create calendar view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Calendar);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Calendar);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('rename and delete linked view', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// rename board view
|
||||
await tester.renameLinkedView(
|
||||
tester.findTabBarLinkViewByViewLayout(ViewLayoutPB.Board),
|
||||
'new board',
|
||||
);
|
||||
final findBoard = tester.findTabBarLinkViewByViewName('new board');
|
||||
expect(findBoard, findsOneWidget);
|
||||
|
||||
// delete the board
|
||||
await tester.deleteDatebaseView(findBoard);
|
||||
expect(tester.findTabBarLinkViewByViewName('new board'), findsNothing);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('delete the last database view', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// delete the board
|
||||
await tester.deleteDatebaseView(
|
||||
tester.findTabBarLinkViewByViewLayout(ViewLayoutPB.Board),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('calendar database view', () {
|
||||
testWidgets('update calendar layout', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
|
||||
|
||||
// open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapDatabaseLayoutButton();
|
||||
await tester.selectDatabaseLayoutType(DatabaseLayoutPB.Board);
|
||||
await tester.assertCurrentDatabaseLayoutType(DatabaseLayoutPB.Board);
|
||||
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapDatabaseLayoutButton();
|
||||
await tester.selectDatabaseLayoutType(DatabaseLayoutPB.Grid);
|
||||
await tester.assertCurrentDatabaseLayoutType(DatabaseLayoutPB.Grid);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('calendar start from day setting', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create calendar view
|
||||
const name = 'calendar';
|
||||
await tester.createNewPageWithName(
|
||||
name: name,
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
|
||||
// Open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapCalendarLayoutSettingButton();
|
||||
|
||||
// select the first day of week is Monday
|
||||
await tester.tapFirstDayOfWeek();
|
||||
await tester.tapFirstDayOfWeekStartFromMonday();
|
||||
|
||||
// Open the other page and open the new calendar page again
|
||||
await tester.openPage(gettingStarted);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 300));
|
||||
await tester.openPage(name, layout: ViewLayoutPB.Calendar);
|
||||
|
||||
// Open setting again and check the start from Monday is selected
|
||||
await tester.tapDatabaseSettingButton();
|
||||
await tester.tapCalendarLayoutSettingButton();
|
||||
await tester.tapFirstDayOfWeek();
|
||||
tester.assertFirstDayOfWeekStartFromMonday();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('creating and editing calendar events', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create the calendar view
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
|
||||
|
||||
// Scroll until today's date cell is visible
|
||||
await tester.scrollToToday();
|
||||
|
||||
// Hover over today's calendar cell
|
||||
await tester.hoverOnTodayCalendarCell();
|
||||
|
||||
// Tap on create new event button
|
||||
await tester.tapAddCalendarEventButton();
|
||||
|
||||
// Make sure that the row details page is opened
|
||||
tester.assertRowDetailPageOpened();
|
||||
|
||||
// Dismiss the row details page
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
tester.assertNumberOfEventsInCalendar(1);
|
||||
|
||||
// Double click on today's calendar cell to create a new event
|
||||
await tester.doubleClickCalendarCell(DateTime.now());
|
||||
|
||||
// Make sure that the row details page is opened
|
||||
tester.assertRowDetailPageOpened();
|
||||
|
||||
// Dismiss the row details page
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
// Make sure that the event is inserted in the cell
|
||||
tester.assertNumberOfEventsInCalendar(2);
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
tester.assertRowDetailPageOpened();
|
||||
|
||||
// Change the title of the event
|
||||
await tester.editTitleInRowDetailPage('hello world');
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
// Make sure that the event is edited
|
||||
tester.assertNumberOfEventsInCalendar(1, title: 'hello world');
|
||||
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 1);
|
||||
tester.assertRowDetailPageOpened();
|
||||
|
||||
// Duplicate the event
|
||||
await tester.tapRowDetailPageDuplicateRowButton();
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
// Check that there are 2 events
|
||||
tester.assertNumberOfEventsInCalendar(2, title: 'hello world');
|
||||
tester.assertNumberOfEventsOnSpecificDay(3, DateTime.now());
|
||||
|
||||
// Delete an event
|
||||
await tester.openCalendarEvent(index: 1);
|
||||
await tester.tapRowDetailPageDeleteRowButton();
|
||||
|
||||
// Check that there is 1 event
|
||||
tester.assertNumberOfEventsInCalendar(1, title: 'hello world');
|
||||
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
||||
});
|
||||
|
||||
testWidgets('rescheduling events', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create the calendar view
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
|
||||
|
||||
// Create a new event on the first of this month
|
||||
final today = DateTime.now();
|
||||
final firstOfThisMonth = DateTime(today.year, today.month, 1);
|
||||
await tester.doubleClickCalendarCell(firstOfThisMonth);
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
// Drag and drop the event onto the next week, same day
|
||||
await tester.dragDropRescheduleCalendarEvent(firstOfThisMonth);
|
||||
|
||||
// Make sure that the event has been rescheduled to the new date
|
||||
final sameDayNextWeek = firstOfThisMonth.add(const Duration(days: 7));
|
||||
tester.assertNumberOfEventsInCalendar(1);
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, sameDayNextWeek);
|
||||
|
||||
// Delete the event
|
||||
await tester.openCalendarEvent(index: 0, date: sameDayNextWeek);
|
||||
await tester.tapRowDetailPageDeleteRowButton();
|
||||
|
||||
// Create a new event in today's calendar cell
|
||||
await tester.scrollToToday();
|
||||
await tester.doubleClickCalendarCell(today);
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
// Make sure that the event is today
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, today);
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
|
||||
// Open the date editor of the event
|
||||
await tester.tapDateCellInRowDetailPage();
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Edit the event's date. To avoid selecting a day outside of the current month, the new date will be one day closer to the middle of the month.
|
||||
final newDate = today.day < 15
|
||||
? today.add(const Duration(days: 1))
|
||||
: today.subtract(const Duration(days: 1));
|
||||
await tester.selectDay(content: newDate.day);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Dismiss the row details page
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
// Make sure that the event is edited
|
||||
tester.assertNumberOfEventsInCalendar(1);
|
||||
tester.assertNumberOfEventsOnSpecificDay(1, newDate);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,445 +0,0 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid cell', () {
|
||||
testWidgets('edit text cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
input: 'hello world',
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'hello world',
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
// Makesure the text cells are filled with the right content when there are
|
||||
// multiple text cell
|
||||
testWidgets('edit multiple text cells', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithName(
|
||||
name: 'my grid',
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
await tester.createField(FieldType.RichText, 'description');
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
input: 'hello',
|
||||
);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
input: 'world',
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'hello',
|
||||
cellIndex: 0,
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'world',
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit number cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Number;
|
||||
|
||||
// Create a number field
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
input: '-1',
|
||||
);
|
||||
// edit the next cell to force the previous cell at row 0 to lose focus
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: fieldType,
|
||||
input: '0.2',
|
||||
);
|
||||
// -1 -> -1
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
content: '-1',
|
||||
);
|
||||
|
||||
// edit the next cell to force the previous cell at row 1 to lose focus
|
||||
await tester.editCell(
|
||||
rowIndex: 2,
|
||||
fieldType: fieldType,
|
||||
input: '.1',
|
||||
);
|
||||
// 0.2 -> 0.2
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 1,
|
||||
fieldType: fieldType,
|
||||
content: '0.2',
|
||||
);
|
||||
|
||||
// edit the next cell to force the previous cell at row 2 to lose focus
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
input: '',
|
||||
);
|
||||
// .1 -> 0.1
|
||||
await tester.assertCellContent(
|
||||
rowIndex: 2,
|
||||
fieldType: fieldType,
|
||||
content: '0.1',
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit checkbox cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.assertCheckboxCell(rowIndex: 0, isSelected: false);
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 0);
|
||||
await tester.assertCheckboxCell(rowIndex: 0, isSelected: true);
|
||||
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 1);
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 2);
|
||||
await tester.assertCheckboxCell(rowIndex: 1, isSelected: true);
|
||||
await tester.assertCheckboxCell(rowIndex: 2, isSelected: true);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit create time cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.CreatedTime;
|
||||
// Create a create time field
|
||||
// The create time field is not editable
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
|
||||
await tester.findDateEditor(findsNothing);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit last time cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.LastEditedTime;
|
||||
// Create a last time field
|
||||
// The last time field is not editable
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
|
||||
await tester.findDateEditor(findsNothing);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit time cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.DateTime;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
// Tap the cell to invoke the field editor
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Toggle include time
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
// Dismiss the cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Turn off include time
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
// Select a date
|
||||
final today = DateTime.now();
|
||||
await tester.selectDay(content: today.day);
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
content: DateFormat('MMM dd, y').format(today),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Toggle include time
|
||||
final now = DateTime.now();
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
content: DateFormat('MMM dd, y HH:mm').format(now),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Change date format
|
||||
await tester.changeDateFormat();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
content: DateFormat('dd/MM/y HH:mm').format(now),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Change time format
|
||||
await tester.changeTimeFormat();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
content: DateFormat('dd/MM/y hh:mm a').format(now),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Clear the date and time
|
||||
await tester.clearDate();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
content: '',
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit single select cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
const fieldType = FieldType.SingleSelect;
|
||||
|
||||
// When create a grid, it will create a single select field by default
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Tap the cell to invoke the selection option editor
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Create a new select option
|
||||
await tester.createOption(name: 'tag 1');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Make sure the option is created and displayed in the cell
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 1',
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Create another select option
|
||||
await tester.createOption(name: 'tag 2');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 2',
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsOneWidget,
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// switch to first option
|
||||
await tester.selectOption(name: 'tag 1');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 1',
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsOneWidget,
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Deselect the currently-selected option
|
||||
await tester.selectOption(name: 'tag 1');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNothing,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit multi select cell', (tester) async {
|
||||
final tags = [
|
||||
'tag 1',
|
||||
'tag 2',
|
||||
'tag 3',
|
||||
'tag 4',
|
||||
];
|
||||
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.MultiSelect;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
|
||||
// Tap the cell to invoke the selection option editor
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Create a new select option
|
||||
await tester.createOption(name: tags.first);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Make sure the option is created and displayed in the cell
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags.first,
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Create some other select options
|
||||
await tester.createOption(name: tags[1]);
|
||||
await tester.createOption(name: tags[2]);
|
||||
await tester.createOption(name: tags[3]);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
for (final tag in tags) {
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tag,
|
||||
);
|
||||
}
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNWidgets(4),
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Deselect all options
|
||||
for (final tag in tags) {
|
||||
await tester.selectOption(name: tag);
|
||||
}
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNothing,
|
||||
);
|
||||
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
await tester.findSelectOptionEditor(findsOneWidget);
|
||||
|
||||
// Select some options
|
||||
await tester.selectOption(name: tags[1]);
|
||||
await tester.selectOption(name: tags[3]);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags[1],
|
||||
);
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags[3],
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNWidgets(2),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/emoji.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid', () {
|
||||
testWidgets('row details page opens', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Make sure that the row page is opened
|
||||
tester.assertRowDetailPageOpened();
|
||||
});
|
||||
|
||||
testWidgets('insert emoji in the row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
await tester.hoverRowBanner();
|
||||
|
||||
await tester.openEmojiPicker();
|
||||
await tester.switchToEmojiList();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// After select the emoji, the EmojiButton will show up
|
||||
await tester.tapButton(find.byType(EmojiButton));
|
||||
});
|
||||
|
||||
testWidgets('update emoji in the row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
await tester.hoverRowBanner();
|
||||
await tester.openEmojiPicker();
|
||||
await tester.switchToEmojiList();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Update existing selected emoji
|
||||
await tester.tapButton(find.byType(EmojiButton));
|
||||
await tester.switchToEmojiList();
|
||||
await tester.tapEmoji('😅');
|
||||
|
||||
// The emoji already displayed in the row banner
|
||||
final emojiText = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == '😅',
|
||||
);
|
||||
|
||||
// The number of emoji should be two. One in the row displayed in the grid
|
||||
// one in the row detail page.
|
||||
expect(emojiText, findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('remove emoji in the row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
await tester.hoverRowBanner();
|
||||
await tester.openEmojiPicker();
|
||||
await tester.switchToEmojiList();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Remove the emoji
|
||||
await tester.tapButton(find.byType(RemoveEmojiButton));
|
||||
final emojiText = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == '😀',
|
||||
);
|
||||
expect(emojiText, findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('create list of fields in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
for (final fieldType in [
|
||||
FieldType.Checklist,
|
||||
FieldType.DateTime,
|
||||
FieldType.Number,
|
||||
FieldType.URL,
|
||||
FieldType.MultiSelect,
|
||||
FieldType.LastEditedTime,
|
||||
FieldType.CreatedTime,
|
||||
FieldType.Checkbox,
|
||||
]) {
|
||||
await tester.tapRowDetailPageCreatePropertyButton();
|
||||
await tester.renameField(fieldType.name);
|
||||
|
||||
// Open the type option menu
|
||||
await tester.tapTypeOptionButton();
|
||||
|
||||
await tester.selectFieldType(fieldType);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// After update the field type, the cells should be updated
|
||||
await tester.findCellByFieldType(fieldType);
|
||||
await tester.scrollRowDetailByOffset(const Offset(0, -50));
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('check document is exist in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Each row detail page should have a document
|
||||
await tester.assertDocumentExistInRowDetailPage();
|
||||
});
|
||||
|
||||
testWidgets('update the content of the document and re-open it',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Wait for the document to be loaded
|
||||
await tester.wait(500);
|
||||
|
||||
// Focus on the editor
|
||||
final textBlock = find.byType(TextBlockComponentWidget);
|
||||
await tester.tapAt(tester.getCenter(textBlock));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Input some text
|
||||
const inputText = 'Hello World';
|
||||
await tester.ime.insertText(inputText);
|
||||
expect(
|
||||
find.textContaining(inputText, findRichText: true),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// Tap outside to dismiss the field
|
||||
await tester.tapAt(Offset.zero);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Re-open the document
|
||||
await tester.openFirstRowDetailPage();
|
||||
expect(
|
||||
find.textContaining(inputText, findRichText: true),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'check if the title wraps properly when a long text is inserted',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Wait for the document to be loaded
|
||||
await tester.wait(500);
|
||||
|
||||
// Focus on the editor
|
||||
final textField = find
|
||||
.descendant(
|
||||
of: find.byType(SimpleDialog),
|
||||
matching: find.byType(TextField),
|
||||
)
|
||||
.first;
|
||||
|
||||
// Input a long text
|
||||
await tester.enterText(textField, 'Long text' * 25);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap outside to dismiss the field
|
||||
await tester.tapAt(Offset.zero);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Check if there is any overflow in the widget tree
|
||||
expect(tester.takeException(), isNull);
|
||||
|
||||
// Re-open the document
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
// Check again if there is any overflow in the widget tree
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('delete row in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
await tester.tapRowDetailPageDeleteRowButton();
|
||||
await tester.tapEscButton();
|
||||
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
});
|
||||
|
||||
testWidgets('duplicate row in row detail page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
await tester.tapRowDetailPageDuplicateRowButton();
|
||||
await tester.tapEscButton();
|
||||
|
||||
await tester.assertNumberOfRowsInGridPage(4);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/database_test_op.dart';
|
||||
import 'util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('database', () {
|
||||
testWidgets('create linked view', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// Create grid view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.grid);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Grid);
|
||||
|
||||
// Create calendar view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.calendar);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Calendar);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('rename and delete linked view', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// rename board view
|
||||
await tester.renameLinkedView(
|
||||
tester.findTabBarLinkViewByViewLayout(ViewLayoutPB.Board),
|
||||
'new board',
|
||||
);
|
||||
final findBoard = tester.findTabBarLinkViewByViewName('new board');
|
||||
expect(findBoard, findsOneWidget);
|
||||
|
||||
// delete the board
|
||||
await tester.deleteDatebaseView(findBoard);
|
||||
expect(tester.findTabBarLinkViewByViewName('new board'), findsNothing);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('delete the last database view', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// delete the board
|
||||
await tester.deleteDatebaseView(
|
||||
tester.findTabBarLinkViewByViewLayout(ViewLayoutPB.Board),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/keyboard.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('document alignment', () {
|
||||
testWidgets('edit alignment in toolbar', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final selection = Selection.single(
|
||||
path: [0],
|
||||
startOffset: 0,
|
||||
endOffset: 1,
|
||||
);
|
||||
// click the first line of the readme
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.updateSelection(selection);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// click the align center
|
||||
await tester.tapButtonWithFlowySvgData(FlowySvgs.toolbar_align_left_s);
|
||||
await tester.tapButtonWithFlowySvgData(FlowySvgs.toolbar_align_center_s);
|
||||
|
||||
// expect to see the align center
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final first = editorState.getNodeAtPath([0])!;
|
||||
expect(first.attributes[blockComponentAlign], 'center');
|
||||
|
||||
// click the align right
|
||||
await tester.tapButtonWithFlowySvgData(FlowySvgs.toolbar_align_center_s);
|
||||
await tester.tapButtonWithFlowySvgData(FlowySvgs.toolbar_align_right_s);
|
||||
expect(first.attributes[blockComponentAlign], 'right');
|
||||
|
||||
// click the align left
|
||||
await tester.tapButtonWithFlowySvgData(FlowySvgs.toolbar_align_right_s);
|
||||
await tester.tapButtonWithFlowySvgData(FlowySvgs.toolbar_align_left_s);
|
||||
expect(first.attributes[blockComponentAlign], 'left');
|
||||
});
|
||||
|
||||
testWidgets('edit alignment using shortcut', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// click the first line of the readme
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final first = editorState.getNodeAtPath([0])!;
|
||||
|
||||
// expect to see text aligned to the right
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
LogicalKeyboardKey.control,
|
||||
LogicalKeyboardKey.shift,
|
||||
LogicalKeyboardKey.keyR,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], rightAlignmentKey);
|
||||
|
||||
// expect to see text aligned to the center
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
LogicalKeyboardKey.control,
|
||||
LogicalKeyboardKey.shift,
|
||||
LogicalKeyboardKey.keyE,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], centerAlignmentKey);
|
||||
|
||||
// expect to see text aligned to the left
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
LogicalKeyboardKey.control,
|
||||
LogicalKeyboardKey.shift,
|
||||
LogicalKeyboardKey.keyL,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], leftAlignmentKey);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('paste in codeblock', () {
|
||||
testWidgets('paste multiple lines in codeblock', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// mock the clipboard
|
||||
const lines = 3;
|
||||
final text = List.generate(lines, (index) => 'line $index').join('\n');
|
||||
AppFlowyClipboard.mockSetData(
|
||||
AppFlowyClipboardData(
|
||||
text: text,
|
||||
),
|
||||
);
|
||||
|
||||
await insertCodeBlockInDocument(tester);
|
||||
|
||||
// paste the text
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyV,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
expect(editorState.document.root.children.length, 1);
|
||||
expect(
|
||||
editorState.getNodeAtPath([0])!.delta!.toPlainText(),
|
||||
text,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// Inserts an codeBlock in the document
|
||||
Future<void> insertCodeBlockInDocument(WidgetTester tester) async {
|
||||
// open the actions menu and insert the codeBlock
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName(
|
||||
LocaleKeys.document_selectionMenu_codeBlock.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
@@ -219,13 +220,79 @@ void main() {
|
||||
expect(node.delta!.toJson(), [
|
||||
{
|
||||
'insert': text,
|
||||
'attributes': {'href': url}
|
||||
'attributes': {'href': url},
|
||||
}
|
||||
]);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// https://github.com/AppFlowy-IO/AppFlowy/issues/3263
|
||||
testWidgets(
|
||||
'paste the image from clipboard when html and image are both available',
|
||||
(tester) async {
|
||||
const html =
|
||||
'''<meta charset='utf-8'><img src="https://user-images.githubusercontent.com/9403740/262918875-603f4adb-58dd-49b5-8201-341d354935fd.png" alt="image"/>''';
|
||||
final image = await rootBundle.load('assets/test/images/sample.png');
|
||||
final bytes = image.buffer.asUint8List();
|
||||
await tester.pasteContent(
|
||||
html: html,
|
||||
image: ('png', bytes),
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
expect(
|
||||
node.attributes[ImageBlockKeys.url],
|
||||
'https://user-images.githubusercontent.com/9403740/262918875-603f4adb-58dd-49b5-8201-341d354935fd.png',
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('paste the html content contains section', (tester) async {
|
||||
const html =
|
||||
'''<meta charset='utf-8'><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;">AppFlowy</strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;">Hello World</strong></span></section>''';
|
||||
await tester.pasteContent(
|
||||
html: html,
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node1 = editorState.getNodeAtPath([0])!;
|
||||
final node2 = editorState.getNodeAtPath([1])!;
|
||||
expect(node1.type, ParagraphBlockKeys.type);
|
||||
expect(node2.type, ParagraphBlockKeys.type);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('paste the html from google translation', (tester) async {
|
||||
const html =
|
||||
'''<meta charset='utf-8'><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;">new force</font></font></strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;">Assessment focus: potential motivations, empathy</font></font></strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><br style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-Line truncated
|
||||
await tester.pasteContent(
|
||||
html: html,
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 8);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'auto convert url to link preview block',
|
||||
(widgetTester) async {
|
||||
const url = 'https://appflowy.io';
|
||||
await widgetTester.pasteContent(
|
||||
plainText: url,
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, LinkPreviewBlockKeys.type);
|
||||
expect(node.attributes[LinkPreviewBlockKeys.url], url);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
extension on WidgetTester {
|
||||
@@ -240,7 +307,7 @@ extension on WidgetTester {
|
||||
await tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await createNewPageWithName();
|
||||
await createNewPageWithNameUnderParent();
|
||||
|
||||
await beforeTest?.call(editor.getCurrentEditorState());
|
||||
|
||||
|
||||
@@ -17,14 +17,14 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// expect to see a new document
|
||||
tester.expectToSeePageName(
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
);
|
||||
// and with one paragraph block
|
||||
expect(find.byType(TextBlockComponentWidget), findsOneWidget);
|
||||
expect(find.byType(ParagraphBlockComponentWidget), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('delete the readme page and restore it', (tester) async {
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
|
||||
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/keyboard.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('insert inline document reference', () {
|
||||
testWidgets('insert by slash menu', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final name = await createDocumentToReference(tester);
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await triggerReferenceDocumentBySlashMenu(tester);
|
||||
|
||||
// Search for prefix of document
|
||||
await enterDocumentText(tester);
|
||||
|
||||
// Select result
|
||||
final optionFinder = find.descendant(
|
||||
of: find.byType(InlineActionsHandler),
|
||||
matching: find.text(name),
|
||||
);
|
||||
|
||||
await tester.tap(optionFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final mentionBlock = find.byType(MentionPageBlock);
|
||||
expect(mentionBlock, findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('insert by `[[` character shortcut', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final name = await createDocumentToReference(tester);
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.ime.insertText('[[');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select result
|
||||
await tester.editor.tapAtMenuItemWithName(name);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final mentionBlock = find.byType(MentionPageBlock);
|
||||
expect(mentionBlock, findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('insert by `+` character shortcut', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final name = await createDocumentToReference(tester);
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.ime.insertText('+');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select result
|
||||
await tester.editor.tapAtMenuItemWithName(name);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final mentionBlock = find.byType(MentionPageBlock);
|
||||
expect(mentionBlock, findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> createDocumentToReference(WidgetTester tester) async {
|
||||
final name = 'document_${uuid()}';
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: ViewLayoutPB.Document,
|
||||
openAfterCreated: false,
|
||||
);
|
||||
|
||||
// This is a workaround since the openAfterCreated
|
||||
// option does not work in createNewPageWithName method
|
||||
await tester.tap(find.byType(SingleInnerViewItem).first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
Future<void> triggerReferenceDocumentBySlashMenu(WidgetTester tester) async {
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Search for referenced document action
|
||||
await enterDocumentText(tester);
|
||||
|
||||
// Select item
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
LogicalKeyboardKey.enter,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> enterDocumentText(WidgetTester tester) async {
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
LogicalKeyboardKey.keyD,
|
||||
LogicalKeyboardKey.keyO,
|
||||
LogicalKeyboardKey.keyC,
|
||||
LogicalKeyboardKey.keyU,
|
||||
LogicalKeyboardKey.keyM,
|
||||
LogicalKeyboardKey.keyE,
|
||||
LogicalKeyboardKey.keyN,
|
||||
LogicalKeyboardKey.keyT,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// +, ... button beside the block component.
|
||||
group('document with option action button', () {
|
||||
testWidgets(
|
||||
'click + to add a block after current selection, and click + and option key to add a block before current selection',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
var editorState = tester.editor.getCurrentEditorState();
|
||||
expect(editorState.getNodeAtPath([1])?.delta?.toPlainText(), isNotEmpty);
|
||||
|
||||
// add a new block after the current selection
|
||||
await tester.editor.hoverAndClickOptionAddButton([0], false);
|
||||
// await tester.pumpAndSettle();
|
||||
expect(editorState.getNodeAtPath([1])?.delta?.toPlainText(), isEmpty);
|
||||
|
||||
// cancel the selection menu
|
||||
await tester.tapAt(Offset.zero);
|
||||
|
||||
await tester.editor.hoverAndClickOptionAddButton([0], true);
|
||||
await tester.pumpAndSettle();
|
||||
expect(editorState.getNodeAtPath([0])?.delta?.toPlainText(), isEmpty);
|
||||
// cancel the selection menu
|
||||
await tester.tapAt(Offset.zero);
|
||||
await tester.tapAt(Offset.zero);
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(name: 'test');
|
||||
await tester.openPage(gettingStarted);
|
||||
|
||||
// check the status again
|
||||
editorState = tester.editor.getCurrentEditorState();
|
||||
expect(editorState.getNodeAtPath([0])?.delta?.toPlainText(), isEmpty);
|
||||
expect(editorState.getNodeAtPath([2])?.delta?.toPlainText(), isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,16 +1,23 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'document_alignment_test.dart' as document_alignment_test;
|
||||
import 'document_codeblock_paste_test.dart' as document_codeblock_paste_test;
|
||||
import 'document_copy_and_paste_test.dart' as document_copy_and_paste_test;
|
||||
import 'document_create_and_delete_test.dart'
|
||||
as document_create_and_delete_test;
|
||||
import 'document_option_action_test.dart' as document_option_action_test;
|
||||
import 'document_text_direction_test.dart' as document_text_direction_test;
|
||||
import 'document_with_cover_image_test.dart' as document_with_cover_image_test;
|
||||
import 'document_with_database_test.dart' as document_with_database_test;
|
||||
import 'document_with_image_block_test.dart' as document_with_image_block_test;
|
||||
import 'document_with_inline_math_equation_test.dart'
|
||||
as document_with_inline_math_equation_test;
|
||||
import 'document_with_inline_page_test.dart' as document_with_inline_page_test;
|
||||
import 'document_with_outline_block_test.dart' as document_with_outline_block;
|
||||
import 'document_with_toggle_list_test.dart' as document_with_toggle_list_test;
|
||||
import 'edit_document_test.dart' as document_edit_test;
|
||||
import 'document_with_outline_block_test.dart' as document_with_outline_block;
|
||||
import 'document_copy_and_paste_test.dart' as document_copy_and_paste_test;
|
||||
import 'document_inline_page_reference_test.dart'
|
||||
as document_inline_page_reference_test;
|
||||
|
||||
void startTesting() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -25,4 +32,10 @@ void startTesting() {
|
||||
document_with_outline_block.main();
|
||||
document_with_toggle_list_test.main();
|
||||
document_copy_and_paste_test.main();
|
||||
document_codeblock_paste_test.main();
|
||||
document_alignment_test.main();
|
||||
document_text_direction_test.main();
|
||||
document_option_action_test.main();
|
||||
document_with_image_block_test.main();
|
||||
document_inline_page_reference_test.main();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('text direction', () {
|
||||
testWidgets(
|
||||
'''no text direction items will be displayed in the default/LTR mode,and three text direction items will be displayed in the RTL mode.''',
|
||||
(tester) async {
|
||||
// combine the two tests into one to avoid the time-consuming process of initializing the app
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final selection = Selection.single(
|
||||
path: [0],
|
||||
startOffset: 0,
|
||||
endOffset: 1,
|
||||
);
|
||||
// click the first line of the readme
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.updateSelection(selection);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// because this icons are defined in the appflowy_editor package, we can't fetch the icons by SVG data. [textDirectionItems]
|
||||
final textDirectionIconNames = [
|
||||
'toolbar/text_direction_auto',
|
||||
'toolbar/text_direction_left',
|
||||
'toolbar/text_direction_right',
|
||||
];
|
||||
// no text direction items in default/LTR mode
|
||||
var button = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SVGIconItemWidget &&
|
||||
textDirectionIconNames.contains(widget.iconName),
|
||||
);
|
||||
expect(button, findsNothing);
|
||||
|
||||
// switch to the RTL mode
|
||||
await tester.switchLayoutDirectionMode(LayoutDirection.rtlLayout);
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.updateSelection(selection);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
button = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SVGIconItemWidget &&
|
||||
textDirectionIconNames.contains(widget.iconName),
|
||||
);
|
||||
expect(button, findsNWidgets(3));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -17,7 +22,6 @@ void main() {
|
||||
|
||||
// Hover over cover toolbar to show 'Add Cover' and 'Add Icon' buttons
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
tester.expectToSeePluginAddCoverAndIconButton();
|
||||
|
||||
// Insert a document cover
|
||||
await tester.editor.tapOnAddCover();
|
||||
@@ -53,15 +57,10 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
tester.expectToSeeDocumentIcon(null);
|
||||
|
||||
// Hover over cover toolbar to show the 'Add Cover' and 'Add Icon' buttons
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
tester.expectToSeePluginAddCoverAndIconButton();
|
||||
tester.expectToSeeDocumentIcon('⭐️');
|
||||
|
||||
// Insert a document icon
|
||||
await tester.editor.tapAddIconButton();
|
||||
await tester.switchToEmojiList();
|
||||
await tester.editor.tapGettingStartedIcon();
|
||||
await tester.tapEmoji('😀');
|
||||
tester.expectToSeeDocumentIcon('😀');
|
||||
|
||||
@@ -73,13 +72,11 @@ void main() {
|
||||
// Add the icon back for further testing
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
await tester.editor.tapAddIconButton();
|
||||
await tester.switchToEmojiList();
|
||||
await tester.tapEmoji('😀');
|
||||
tester.expectToSeeDocumentIcon('😀');
|
||||
|
||||
// Change the document icon
|
||||
await tester.editor.tapOnIconWidget();
|
||||
await tester.switchToEmojiList();
|
||||
await tester.tapEmoji('😅');
|
||||
tester.expectToSeeDocumentIcon('😅');
|
||||
|
||||
@@ -93,19 +90,15 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
tester.expectToSeeDocumentIcon(null);
|
||||
tester.expectToSeeDocumentIcon('⭐️');
|
||||
tester.expectToSeeNoDocumentCover();
|
||||
|
||||
// Hover over cover toolbar to show the 'Add Cover' and 'Add Icon' buttons
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
tester.expectToSeePluginAddCoverAndIconButton();
|
||||
|
||||
// Insert a document icon
|
||||
await tester.editor.tapAddIconButton();
|
||||
await tester.switchToEmojiList();
|
||||
await tester.editor.tapGettingStartedIcon();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Insert a document cover
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
await tester.editor.tapOnAddCover();
|
||||
|
||||
// Expect to see the icon and cover at the same time
|
||||
@@ -116,5 +109,48 @@ void main() {
|
||||
await tester.editor.hoverOnCoverToolbar();
|
||||
tester.expectToSeeEmptyDocumentHeaderToolbar();
|
||||
});
|
||||
|
||||
testWidgets('shuffle icon', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.editor.tapGettingStartedIcon();
|
||||
|
||||
// click the shuffle button
|
||||
await tester.tapButton(
|
||||
find.byTooltip(LocaleKeys.emoji_random.tr()),
|
||||
);
|
||||
tester.expectDocumentIconNotNull();
|
||||
});
|
||||
|
||||
testWidgets('change skin tone', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.editor.tapGettingStartedIcon();
|
||||
|
||||
final searchEmojiTextField = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is TextField &&
|
||||
widget.decoration!.hintText == LocaleKeys.emoji_search.tr(),
|
||||
);
|
||||
await tester.enterText(
|
||||
searchEmojiTextField,
|
||||
'hand',
|
||||
);
|
||||
|
||||
// change skin tone
|
||||
await tester.editor.changeEmojiSkinTone(EmojiSkinTone.dark);
|
||||
|
||||
// select an icon with skin tone
|
||||
const hand = '👋🏿';
|
||||
await tester.tapEmoji(hand);
|
||||
tester.expectToSeeDocumentIcon(hand);
|
||||
tester.expectViewHasIcon(
|
||||
gettingStarted,
|
||||
ViewLayoutPB.Document,
|
||||
hand,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/board/presentation/board_page.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart';
|
||||
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/board_page.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/calendar_page.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/text_cell/text_cell.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
@@ -31,6 +32,18 @@ void main() {
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// https://github.com/AppFlowy-IO/AppFlowy/issues/3533
|
||||
// test: the selection of editor should be clear when editing the grid
|
||||
await tester.editor.updateSelection(
|
||||
Selection.collapsed(
|
||||
Position(path: [1]),
|
||||
),
|
||||
);
|
||||
final gridTextCell = find.byType(GridTextCell).first;
|
||||
await tester.tapButton(gridTextCell);
|
||||
|
||||
expect(tester.editor.getCurrentEditorState().selection, isNull);
|
||||
});
|
||||
|
||||
testWidgets('insert a referenced board', (tester) async {
|
||||
@@ -123,13 +136,13 @@ Future<void> insertReferenceDatabase(
|
||||
// create a new grid
|
||||
final id = uuid();
|
||||
final name = '${layout.name}_$id';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: layout,
|
||||
openAfterCreated: false,
|
||||
);
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'insert_a_reference_${layout.name}',
|
||||
layout: ViewLayoutPB.Document,
|
||||
openAfterCreated: true,
|
||||
@@ -142,7 +155,7 @@ Future<void> insertReferenceDatabase(
|
||||
layout.referencedMenuName,
|
||||
);
|
||||
|
||||
final linkToPageMenu = find.byType(LinkToPageMenu);
|
||||
final linkToPageMenu = find.byType(InlineActionsHandler);
|
||||
expect(linkToPageMenu, findsOneWidget);
|
||||
final referencedDatabase = find.descendant(
|
||||
of: linkToPageMenu,
|
||||
@@ -158,7 +171,7 @@ Future<void> createInlineDatabase(
|
||||
) async {
|
||||
// create a new document
|
||||
final documentName = 'insert_a_inline_${layout.name}';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: documentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
openAfterCreated: true,
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/custom_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_placeholder.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/unsplash_image_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide UploadImageMenu;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:run_with_network_images/run_with_network_images.dart';
|
||||
|
||||
import '../util/mock/mock_file_picker.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('image block in document', () {
|
||||
testWidgets('insert an image from local file', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Image');
|
||||
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(ImagePlaceholder), findsOneWidget);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(ImagePlaceholder),
|
||||
matching: find.byType(AppFlowyPopover),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
final image = await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final tempDirectory = await getTemporaryDirectory();
|
||||
final imagePath = p.join(tempDirectory.path, 'sample.jpeg');
|
||||
final file = File(imagePath)
|
||||
..writeAsBytesSync(image.buffer.asUint8List());
|
||||
|
||||
mockPickFilePaths(
|
||||
paths: [imagePath],
|
||||
);
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(ResizableImage), findsOneWidget);
|
||||
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
expect(node.attributes[ImageBlockKeys.url], isNotEmpty);
|
||||
|
||||
// remove the temp file
|
||||
file.deleteSync();
|
||||
});
|
||||
|
||||
testWidgets('insert an image from network', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Image');
|
||||
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(ImagePlaceholder), findsOneWidget);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(ImagePlaceholder),
|
||||
matching: find.byType(AppFlowyPopover),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
);
|
||||
const url =
|
||||
'https://images.unsplash.com/photo-1469474968028-56623f02e42e?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=david-marcu-78A265wPiO4-unsplash.jpg&w=640';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
url,
|
||||
);
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.text(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
findRichText: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(ResizableImage), findsOneWidget);
|
||||
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
expect(node.attributes[ImageBlockKeys.url], url);
|
||||
});
|
||||
|
||||
testWidgets('insert an image from unsplash', (tester) async {
|
||||
await runWithNetworkImages(() async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Image');
|
||||
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(ImagePlaceholder), findsOneWidget);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(ImagePlaceholder),
|
||||
matching: find.byType(AppFlowyPopover),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
'Unsplash',
|
||||
);
|
||||
expect(find.byType(UnsplashImageWidget), findsOneWidget);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
@@ -20,8 +20,8 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
name: LocaleKeys.document_plugins_createInlineMathEquation.tr(),
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'math equation',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@@ -66,8 +66,8 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
name: LocaleKeys.document_plugins_createInlineMathEquation.tr(),
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'math equation',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -15,7 +15,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await insertingInlinePage(tester, ViewLayoutPB.Grid);
|
||||
await insertInlinePage(tester, ViewLayoutPB.Grid);
|
||||
|
||||
final mentionBlock = find.byType(MentionPageBlock);
|
||||
expect(mentionBlock, findsOneWidget);
|
||||
@@ -26,7 +26,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await insertingInlinePage(tester, ViewLayoutPB.Board);
|
||||
await insertInlinePage(tester, ViewLayoutPB.Board);
|
||||
|
||||
final mentionBlock = find.byType(MentionPageBlock);
|
||||
expect(mentionBlock, findsOneWidget);
|
||||
@@ -37,7 +37,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await insertingInlinePage(tester, ViewLayoutPB.Calendar);
|
||||
await insertInlinePage(tester, ViewLayoutPB.Calendar);
|
||||
|
||||
final mentionBlock = find.byType(MentionPageBlock);
|
||||
expect(mentionBlock, findsOneWidget);
|
||||
@@ -48,7 +48,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await insertingInlinePage(tester, ViewLayoutPB.Document);
|
||||
await insertInlinePage(tester, ViewLayoutPB.Document);
|
||||
|
||||
final mentionBlock = find.byType(MentionPageBlock);
|
||||
expect(mentionBlock, findsOneWidget);
|
||||
@@ -59,7 +59,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final pageName = await insertingInlinePage(tester, ViewLayoutPB.Document);
|
||||
final pageName = await insertInlinePage(tester, ViewLayoutPB.Document);
|
||||
|
||||
// rename
|
||||
const newName = 'RenameToNewPageName';
|
||||
@@ -78,7 +78,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final pageName = await insertingInlinePage(tester, ViewLayoutPB.Grid);
|
||||
final pageName = await insertInlinePage(tester, ViewLayoutPB.Grid);
|
||||
|
||||
// rename
|
||||
await tester.hoverOnPageName(
|
||||
@@ -98,27 +98,31 @@ void main() {
|
||||
}
|
||||
|
||||
/// Insert a referenced database of [layout] into the document
|
||||
Future<String> insertingInlinePage(
|
||||
Future<String> insertInlinePage(
|
||||
WidgetTester tester,
|
||||
ViewLayoutPB layout,
|
||||
) async {
|
||||
// create a new grid
|
||||
final id = uuid();
|
||||
final name = '${layout.name}_$id';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: layout,
|
||||
openAfterCreated: false,
|
||||
);
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'insert_a_inline_page_${layout.name}',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
// insert a inline page
|
||||
await tester.editor.showAtMenu();
|
||||
await tester.editor.tapAtMenuItemWithName(name);
|
||||
|
||||
return name;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -24,7 +24,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/outline/outline_block_component.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
@@ -15,7 +15,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'outline_test',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
@@ -32,7 +32,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'outline_test',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -15,14 +15,23 @@ void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('toggle list in document', () {
|
||||
Finder findToggleListIcon({
|
||||
required bool isExpanded,
|
||||
}) {
|
||||
final turns = isExpanded ? 0.25 : 0.0;
|
||||
return find.byWidgetPredicate(
|
||||
(widget) => widget is AnimatedRotation && widget.turns == turns,
|
||||
);
|
||||
}
|
||||
|
||||
void expectToggleListOpened() {
|
||||
expect(find.byIcon(Icons.arrow_drop_down), findsOneWidget);
|
||||
expect(find.byIcon(Icons.arrow_right), findsNothing);
|
||||
expect(findToggleListIcon(isExpanded: true), findsOneWidget);
|
||||
expect(findToggleListIcon(isExpanded: false), findsNothing);
|
||||
}
|
||||
|
||||
void expectToggleListClosed() {
|
||||
expect(find.byIcon(Icons.arrow_drop_down), findsNothing);
|
||||
expect(find.byIcon(Icons.arrow_right), findsOneWidget);
|
||||
expect(findToggleListIcon(isExpanded: false), findsOneWidget);
|
||||
expect(findToggleListIcon(isExpanded: true), findsNothing);
|
||||
}
|
||||
|
||||
testWidgets('convert > to toggle list, and click the icon to close it',
|
||||
@@ -31,7 +40,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@@ -63,7 +72,7 @@ void main() {
|
||||
expect(find.text(text2, findRichText: true), findsOneWidget);
|
||||
|
||||
// Click the toggle list icon to close it
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_drop_down);
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_right);
|
||||
await tester.tapButton(toggleListIcon);
|
||||
|
||||
// expect the toggle list to be closed
|
||||
@@ -77,7 +86,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@@ -88,7 +97,7 @@ void main() {
|
||||
await tester.ime.insertText('> $text');
|
||||
|
||||
// Click the toggle list icon to close it
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_drop_down);
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_right);
|
||||
await tester.tapButton(toggleListIcon);
|
||||
|
||||
// Press the enter key
|
||||
@@ -116,7 +125,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@@ -153,7 +162,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
@@ -164,7 +173,7 @@ void main() {
|
||||
|
||||
// Press the enter key
|
||||
// Click the toggle list icon to close it
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_drop_down);
|
||||
final toggleListIcon = find.byIcon(Icons.arrow_right);
|
||||
await tester.tapButton(toggleListIcon);
|
||||
|
||||
await tester.editor.updateSelection(
|
||||
@@ -187,7 +196,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -18,7 +18,7 @@ void main() {
|
||||
|
||||
// create a new document called Sample
|
||||
const pageName = 'Sample';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: pageName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
@@ -75,7 +75,7 @@ void main() {
|
||||
|
||||
// create a new document called Sample
|
||||
const pageName = 'Sample';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: pageName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
@@ -104,7 +104,7 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
const _sample = r'''
|
||||
const _sample = '''
|
||||
# Heading 1
|
||||
## Heading 2
|
||||
### Heading 3
|
||||
@@ -113,9 +113,9 @@ const _sample = r'''
|
||||
|
||||
[] Type followed by bullet or num to create a list.
|
||||
|
||||
[x] Click `+ New Page` button at the bottom of your sidebar to add a new page.
|
||||
[x] Click `New Page` button at the bottom of your sidebar to add a new page.
|
||||
|
||||
[] Click `+` next to any page title in the sidebar to quickly add a new subpage, `Document`, `Grid`, or `Kanban Board`.
|
||||
[] Click the plus sign next to any page title in the sidebar to quickly add a new subpage, `Document`, `Grid`, or `Kanban Board`.
|
||||
---
|
||||
* bulleted list 1
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/util.dart';
|
||||
|
||||
// This test is meaningless, just for preventing the CI from failing.
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Empty', () {
|
||||
testWidgets('toggle theme mode', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -19,7 +19,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
@@ -71,7 +71,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'util/mock/mock_openai_repository.dart';
|
||||
import 'util/util.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart';
|
||||
import 'package:appflowy/user/application/user_settings_service.dart';
|
||||
import 'package:appflowy/workspace/application/settings/date_time/date_format_ext.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/date_time.pbenum.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:calendar_view/calendar_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/base.dart';
|
||||
import '../util/common_operations.dart';
|
||||
import '../util/editor_test_operations.dart';
|
||||
import '../util/keyboard.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Reminder in Document', () {
|
||||
testWidgets('Add reminder for tomorrow, and include time', (tester) async {
|
||||
const time = "23:59";
|
||||
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
final dateTimeSettings =
|
||||
await UserSettingsBackendService().getDateTimeSettings();
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.getCurrentEditorState().insertNewLine();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger iline action menu and type 'remind tomorrow'
|
||||
final tomorrow = await _insertReminderTomorrow(tester);
|
||||
|
||||
Node node = tester.editor.getCurrentEditorState().getNodeAtPath([1])!;
|
||||
Map<String, dynamic> mentionAttr =
|
||||
node.delta!.first.attributes![MentionBlockKeys.mention];
|
||||
|
||||
expect(node.type, 'paragraph');
|
||||
expect(mentionAttr['type'], MentionType.reminder.name);
|
||||
expect(mentionAttr['date'], tomorrow.toIso8601String());
|
||||
|
||||
await tester.tap(
|
||||
find.text(dateTimeSettings.dateFormat.formatDate(tomorrow, false)),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byType(Toggle));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byType(FlowyTextField), time);
|
||||
|
||||
// Leave text field to submit
|
||||
await tester.tap(find.text(LocaleKeys.grid_field_includeTime.tr()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
node = tester.editor.getCurrentEditorState().getNodeAtPath([1])!;
|
||||
mentionAttr = node.delta!.first.attributes![MentionBlockKeys.mention];
|
||||
|
||||
final tomorrowWithTime =
|
||||
_dateWithTime(dateTimeSettings.timeFormat, tomorrow, time);
|
||||
|
||||
expect(node.type, 'paragraph');
|
||||
expect(mentionAttr['type'], MentionType.reminder.name);
|
||||
expect(mentionAttr['date'], tomorrowWithTime.toIso8601String());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<DateTime> _insertReminderTomorrow(WidgetTester tester) async {
|
||||
await tester.editor.showAtMenu();
|
||||
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
LogicalKeyboardKey.keyR,
|
||||
LogicalKeyboardKey.keyE,
|
||||
LogicalKeyboardKey.keyM,
|
||||
LogicalKeyboardKey.keyI,
|
||||
LogicalKeyboardKey.keyN,
|
||||
LogicalKeyboardKey.keyD,
|
||||
LogicalKeyboardKey.space,
|
||||
LogicalKeyboardKey.keyT,
|
||||
LogicalKeyboardKey.keyO,
|
||||
LogicalKeyboardKey.keyM,
|
||||
LogicalKeyboardKey.keyO,
|
||||
LogicalKeyboardKey.keyR,
|
||||
LogicalKeyboardKey.keyR,
|
||||
LogicalKeyboardKey.keyO,
|
||||
LogicalKeyboardKey.keyW,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[LogicalKeyboardKey.enter],
|
||||
tester: tester,
|
||||
);
|
||||
|
||||
return DateTime.now().add(const Duration(days: 1)).withoutTime;
|
||||
}
|
||||
|
||||
DateTime _dateWithTime(UserTimeFormatPB format, DateTime date, String time) {
|
||||
final t = format == UserTimeFormatPB.TwelveHour
|
||||
? DateFormat.jm().parse(time)
|
||||
: DateFormat.Hm().parse(time);
|
||||
|
||||
return DateTime.parse(
|
||||
'${date.year}${_padZeroLeft(date.month)}${_padZeroLeft(date.day)} ${_padZeroLeft(t.hour)}:${_padZeroLeft(t.minute)}',
|
||||
);
|
||||
}
|
||||
|
||||
String _padZeroLeft(int a) => a.toString().padLeft(2, '0');
|
||||
@@ -1,26 +1,29 @@
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'database_calendar_test.dart' as database_calendar_test;
|
||||
import 'database_cell_test.dart' as database_cell_test;
|
||||
import 'database_field_test.dart' as database_field_test;
|
||||
import 'database_filter_test.dart' as database_filter_test;
|
||||
import 'database_row_page_test.dart' as database_row_page_test;
|
||||
import 'database_row_test.dart' as database_row_test;
|
||||
import 'database_setting_test.dart' as database_setting_test;
|
||||
import 'database_share_test.dart' as database_share_test;
|
||||
import 'database_sort_test.dart' as database_sort_test;
|
||||
import 'database_view_test.dart' as database_view_test;
|
||||
import 'document/document_test_runner.dart' as document_test_runner;
|
||||
import 'import_files_test.dart' as import_files_test;
|
||||
import 'share_markdown_test.dart' as share_markdown_test;
|
||||
import 'switch_folder_test.dart' as switch_folder_test;
|
||||
import 'sidebar/sidebar_test_runner.dart' as sidebar_test_runner;
|
||||
import 'board/board_test_runner.dart' as board_test_runner;
|
||||
import 'tabs_test.dart' as tabs_test;
|
||||
import 'hotkeys_test.dart' as hotkeys_test;
|
||||
import 'appearance_settings_test.dart' as appearance_test_runner;
|
||||
import 'auth/auth_test.dart' as auth_test_runner;
|
||||
import 'board/board_test_runner.dart' as board_test_runner;
|
||||
import 'database/database_calendar_test.dart' as database_calendar_test;
|
||||
import 'database/database_cell_test.dart' as database_cell_test;
|
||||
import 'database/database_field_settings_test.dart'
|
||||
as database_field_settings_test;
|
||||
import 'database/database_field_test.dart' as database_field_test;
|
||||
import 'database/database_filter_test.dart' as database_filter_test;
|
||||
import 'database/database_row_page_test.dart' as database_row_page_test;
|
||||
import 'database/database_row_test.dart' as database_row_test;
|
||||
import 'database/database_setting_test.dart' as database_setting_test;
|
||||
import 'database/database_share_test.dart' as database_share_test;
|
||||
import 'database/database_sort_test.dart' as database_sort_test;
|
||||
import 'database/database_view_test.dart' as database_view_test;
|
||||
import 'document/document_test_runner.dart' as document_test_runner;
|
||||
import 'empty_test.dart' as first_test;
|
||||
import 'hotkeys_test.dart' as hotkeys_test;
|
||||
import 'import_files_test.dart' as import_files_test;
|
||||
import 'settings/settings_runner.dart' as settings_test_runner;
|
||||
import 'share_markdown_test.dart' as share_markdown_test;
|
||||
import 'sidebar/sidebar_test_runner.dart' as sidebar_test_runner;
|
||||
import 'switch_folder_test.dart' as switch_folder_test;
|
||||
import 'tabs_test.dart' as tabs_test;
|
||||
// import 'auth/supabase_auth_test.dart' as supabase_auth_test_runner;
|
||||
|
||||
/// The main task runner for all integration tests in AppFlowy.
|
||||
///
|
||||
@@ -29,8 +32,12 @@ import 'auth/auth_test.dart' as auth_test_runner;
|
||||
/// If flutter/flutter#101031 is resolved, this file can be removed completely.
|
||||
/// Once removed, the integration_test.yaml must be updated to exclude this as
|
||||
/// as the test target.
|
||||
void main() {
|
||||
Future<void> main() async {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// This test must be run first, otherwise the CI will fail.
|
||||
first_test.main();
|
||||
|
||||
switch_folder_test.main();
|
||||
share_markdown_test.main();
|
||||
import_files_test.main();
|
||||
@@ -47,6 +54,7 @@ void main() {
|
||||
// Database integration tests
|
||||
database_cell_test.main();
|
||||
database_field_test.main();
|
||||
database_field_settings_test.main();
|
||||
database_share_test.main();
|
||||
database_row_page_test.main();
|
||||
database_row_test.main();
|
||||
@@ -65,9 +73,8 @@ void main() {
|
||||
// Appearance integration test
|
||||
appearance_test_runner.main();
|
||||
|
||||
if (isSupabaseEnabled) {
|
||||
auth_test_runner.main();
|
||||
}
|
||||
// User settings
|
||||
settings_test_runner.main();
|
||||
|
||||
// board_test.main();
|
||||
// empty_document_test.main();
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board add row test', () {
|
||||
testWidgets('Add card from header', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.notifications);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final switchFinder = find.byType(Switch);
|
||||
|
||||
// Defaults to enabled
|
||||
Switch switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, true);
|
||||
|
||||
// Disable
|
||||
await tester.tap(switchFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, false);
|
||||
|
||||
// Enable again
|
||||
await tester.tap(switchFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'notifications_settings_test.dart' as notifications_settings_test;
|
||||
import 'user_language_test.dart' as user_language_test;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
notifications_settings_test.main();
|
||||
user_language_test.main();
|
||||
}
|
||||