Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e81a2ff577 | ||
|
|
b356927e60 | ||
|
|
1894409dc3 | ||
|
|
f29f1bae32 | ||
|
|
45054e7639 | ||
|
|
3b51a6e6be | ||
|
|
747fe40648 | ||
|
|
60fc5bb2c8 | ||
|
|
9d71464f1a | ||
|
|
92ceb2cd7d | ||
|
|
0db21b91a3 | ||
|
|
88e9d63a13 | ||
|
|
3129fa6cc1 | ||
|
|
db5372c18e | ||
|
|
b781c9aa0f | ||
|
|
a9c6b80f4a | ||
|
|
271f781cce | ||
|
|
ef4bce25d8 | ||
|
|
95c4cb241a | ||
|
|
e20d938ce3 | ||
|
|
a515715543 | ||
|
|
fda70ff560 | ||
|
|
08938b8c70 | ||
|
|
250f29f325 | ||
|
|
d117cd6b5b | ||
|
|
ca93cb20ec | ||
|
|
8c1d0106dd | ||
|
|
5cbc8b1e18 | ||
|
|
274742e334 | ||
|
|
792f56db79 | ||
|
|
458d3f971b | ||
|
|
2ffd6bf7ea | ||
|
|
722691c922 | ||
|
|
9746852b5f | ||
|
|
5b030303a6 | ||
|
|
1e4f342fc3 | ||
|
|
4f0379bdd7 | ||
|
|
ce261c9128 | ||
|
|
20391bfec4 | ||
|
|
9d28360887 | ||
|
|
99f9cb2a09 | ||
|
|
247405ff51 | ||
|
|
d27e2179cc | ||
|
|
100cc19747 | ||
|
|
fa66a119ed | ||
|
|
a9787546b4 | ||
|
|
5cbe90c79f | ||
|
|
a143a030a9 | ||
|
|
caede19267 | ||
|
|
d410ffbaa8 | ||
|
|
0483e305e7 | ||
|
|
f6242f1cc7 | ||
|
|
18c7fff34b | ||
|
|
792573f46d | ||
|
|
e9d7d0b7b3 | ||
|
|
0b7ae734a1 | ||
|
|
c0aef8f4fe | ||
|
|
55c97b56a3 | ||
|
|
86a0569d84 | ||
|
|
5b3b0e54d9 | ||
|
|
f1e380c276 | ||
|
|
ea93f0e141 | ||
|
|
c1433ce38a | ||
|
|
831586293e | ||
|
|
5c5b6d02e8 | ||
|
|
8937856d36 | ||
|
|
63c22feb8f | ||
|
|
05a06980b9 | ||
|
|
7be29c04a2 | ||
|
|
5a9a1e60e6 | ||
|
|
4811e65efa | ||
|
|
72a23bfe82 | ||
|
|
ed78cb4efc | ||
|
|
d2cc799861 | ||
|
|
2e7a97c8a2 | ||
|
|
64cd7d41ca | ||
|
|
0e3ffa8fd5 | ||
|
|
be00980fdd | ||
|
|
7fbaf204ef | ||
|
|
acc03b8cc4 | ||
|
|
747abba87f | ||
|
|
e88fb533c8 | ||
|
|
3f896ad64f | ||
|
|
6591d5de52 | ||
|
|
a1abcd7626 | ||
|
|
18a355601a | ||
|
|
c2fdfb6e89 | ||
|
|
baa7c8d826 | ||
|
|
8105da1c2b | ||
|
|
827e41348b | ||
|
|
e239ba46aa | ||
|
|
2554ba81b5 | ||
|
|
84d171b1db | ||
|
|
38902ed599 | ||
|
|
2cdee386c2 | ||
|
|
90516b6adc | ||
|
|
c4acba8216 | ||
|
|
4404e0d727 | ||
|
|
48c6c635f7 | ||
|
|
332f40c915 | ||
|
|
2b32f0eff0 | ||
|
|
7d20ca5a77 | ||
|
|
0a0f2adf76 | ||
|
|
4852e5c8d4 | ||
|
|
65f1db776b | ||
|
|
38c3e700e9 | ||
|
|
15cb1b5f19 | ||
|
|
1bf1d03676 | ||
|
|
f612d9b701 | ||
|
|
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 |
No files matched your search
@@ -0,0 +1,29 @@
|
||||
name: Build Bot
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
dispatch_slash_command:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# get build name from pubspec.yaml
|
||||
- name: Get build version
|
||||
working-directory: frontend/appflowy_flutter
|
||||
id: get_build_name
|
||||
run: |
|
||||
echo "fetching version from pubspec.yaml..."
|
||||
echo "build_name=$(grep 'version: ' pubspec.yaml | awk '{print $2}')" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- uses: peter-evans/slash-command-dispatch@v4
|
||||
with:
|
||||
token: ${{ secrets.PAT }}
|
||||
commands: build
|
||||
static-args: |
|
||||
ref=refs/pull/${{ github.event.issue.number }}/head
|
||||
build_name=${{ steps.get_build_name.outputs.build_name }}
|
||||
@@ -0,0 +1,42 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: [build-command]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: notify appflowy_builder
|
||||
run: |
|
||||
platform=${{ github.event.client_payload.slash_command.args.unnamed.arg1 }}
|
||||
build_name=${{ github.event.client_payload.slash_command.args.named.build_name }}
|
||||
branch=${{ github.event.client_payload.slash_command.args.named.ref }}
|
||||
build_type=""
|
||||
arch=""
|
||||
|
||||
if [ "$platform" = "android" ]; then
|
||||
build_type="apk"
|
||||
elif [ "$platform" = "macos" ]; then
|
||||
arch="universal"
|
||||
fi
|
||||
|
||||
params=$(jq -n \
|
||||
--arg ref "main" \
|
||||
--arg repo "LucasXu0/AppFlowy" \
|
||||
--arg branch "$branch" \
|
||||
--arg build_name "$build_name" \
|
||||
--arg build_type "$build_type" \
|
||||
--arg arch "$arch" \
|
||||
'{ref: $ref, inputs: {repo: $repo, branch: $branch, build_name: $build_name, build_type: $build_type, arch: $arch}} | del(.inputs | .. | select(. == ""))')
|
||||
|
||||
echo "params: $params"
|
||||
|
||||
curl -L \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/AppFlowy-IO/AppFlowy-Builder/actions/workflows/$platform.yaml/dispatches \
|
||||
-d "$params"
|
||||
@@ -4,27 +4,25 @@ 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:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.10.4"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
RUST_TOOLCHAIN: "1.75"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
concurrency:
|
||||
@@ -76,7 +74,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -108,21 +106,19 @@ jobs:
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-core-dev
|
||||
run: cargo make --profile ${{ matrix.flutter_profile }} appflowy-core-dev
|
||||
|
||||
- name: Run code generation
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make code_generation
|
||||
run: cargo make code_generation
|
||||
|
||||
- name: Flutter Analyzer
|
||||
working-directory: frontend/appflowy_flutter
|
||||
run: |
|
||||
flutter analyze .
|
||||
run: flutter analyze .
|
||||
|
||||
- name: Compress appflowy_flutter
|
||||
run: tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter
|
||||
run: |
|
||||
tar -czf appflowy_flutter.tar.gz frontend/appflowy_flutter
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
@@ -135,11 +131,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest]
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- os: macos-latest
|
||||
flutter_profile: development-mac-x86_64
|
||||
target: x86_64-apple-darwin
|
||||
- os: ubuntu-latest
|
||||
flutter_profile: development-linux-x86_64
|
||||
target: x86_64-unknown-linux-gnu
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
@@ -159,7 +155,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -176,36 +172,136 @@ jobs:
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "macOS" ]; then
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
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
|
||||
flutter config --enable-macos-desktop
|
||||
cargo make --profile ${{ matrix.flutter_profile }} appflowy-dev
|
||||
cargo make dart_unit_test
|
||||
else
|
||||
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
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: Wandalen/wretry.action@v1.0.36
|
||||
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:
|
||||
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
|
||||
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 compose pull
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
|
||||
- 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
|
||||
docker ps -a
|
||||
flutter test integration_test/cloud/cloud_runner.dart -d Linux --coverage
|
||||
shell: bash
|
||||
|
||||
integration_test:
|
||||
needs: [prepare]
|
||||
@@ -213,14 +309,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest,windows-latest]
|
||||
os: [ubuntu-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:
|
||||
@@ -240,7 +333,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
@@ -274,13 +367,12 @@ jobs:
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompress appflowy_flutter
|
||||
- name: Uncompressed appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
- name: Run code generation
|
||||
- name: Run flutter pub get
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make code_generation
|
||||
run: cargo make pub_get
|
||||
|
||||
- name: Run Flutter integration tests
|
||||
working-directory: frontend/appflowy_flutter
|
||||
@@ -296,21 +388,6 @@ jobs:
|
||||
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
|
||||
|
||||
build:
|
||||
needs: [prepare]
|
||||
if: github.event.pull_request.draft != true
|
||||
@@ -347,7 +424,7 @@ jobs:
|
||||
id: flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
channel: "beta"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
@@ -381,15 +458,9 @@ jobs:
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
|
||||
- name: Uncompress appflowy_flutter
|
||||
- name: Uncompressed appflowy_flutter
|
||||
run: tar -xf appflowy_flutter.tar.gz
|
||||
|
||||
- name: Run code generation
|
||||
if: matrix.os == 'windows-latest'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make code_generation
|
||||
|
||||
- name: Build flutter product
|
||||
working-directory: frontend
|
||||
run: |
|
||||
|
||||
@@ -18,8 +18,7 @@ on:
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.10.1"
|
||||
RUST_TOOLCHAIN: "1.70"
|
||||
FLUTTER_VERSION: "3.18.0-0.2.pre"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -46,32 +45,31 @@ jobs:
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
sudo docker image prune --all --force
|
||||
sudo rm -rf /opt/hostedtoolcache/codeQL
|
||||
sudo rm -rf ${GITHUB_WORKSPACE}/.git
|
||||
sudo rm -rf $ANDROID_HOME/ndk
|
||||
|
||||
- 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"
|
||||
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
|
||||
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:
|
||||
@@ -110,4 +108,4 @@ jobs:
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
run: |
|
||||
cargo make --profile development-android appflowy-android-dev
|
||||
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:
|
||||
@@ -57,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
|
||||
@@ -70,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: |
|
||||
@@ -149,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
|
||||
@@ -162,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: |
|
||||
@@ -255,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
|
||||
@@ -266,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: |
|
||||
@@ -281,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: |
|
||||
@@ -345,6 +327,8 @@ jobs:
|
||||
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
|
||||
LINUX_PACKAGE_TMP_APPIMAGE_NAME: AppFlowy-${{ github.ref_name }}-x86_64.AppImage
|
||||
LINUX_PACKAGE_APPIMAGE_NAME: AppFlowy-${{ github.ref_name }}-linux-x86_64.AppImage
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -364,9 +348,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
|
||||
@@ -377,11 +360,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: |
|
||||
@@ -419,11 +397,15 @@ jobs:
|
||||
- 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: Build Linux package (.AppImage)
|
||||
working-directory: frontend
|
||||
continue-on-error: true
|
||||
run: |
|
||||
sh scripts/linux_distribution/appimage/build_appimage.sh ${{ github.ref_name }}
|
||||
cp -r ${{ env.LINUX_PACKAGE_TMP_APPIMAGE_NAME }} ${{ env.LINUX_PACKAGE_APPIMAGE_NAME }}
|
||||
|
||||
- name: Upload Asset
|
||||
id: upload-release-asset
|
||||
@@ -458,6 +440,17 @@ jobs:
|
||||
asset_name: ${{ env.LINUX_PACKAGE_RPM_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
- name: Upload AppImage package
|
||||
id: upload-release-asset-install-package-appimage
|
||||
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_APPIMAGE_NAME }}
|
||||
asset_name: ${{ env.LINUX_PACKAGE_APPIMAGE_NAME }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
build-for-docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -8,7 +8,6 @@ on:
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- "shared-lib/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -17,15 +16,13 @@ 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
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
@@ -52,30 +49,42 @@ 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: Checkout appflowy cloud code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: AppFlowy-IO/AppFlowy-Cloud
|
||||
path: AppFlowy-Cloud
|
||||
depth: 1
|
||||
|
||||
- name: Create .env file in event-integration
|
||||
working-directory: frontend/rust-lib/event-integration
|
||||
- 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,dart" -- --nocapture
|
||||
|
||||
|
||||
- name: rustfmt rust-lib
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: frontend/rust-lib/
|
||||
|
||||
- name: clippy rust-lib
|
||||
run: cargo clippy --all
|
||||
working-directory: frontend/rust-lib
|
||||
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 }}
|
||||
|
||||
@@ -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 build_release_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,7 +35,7 @@ pubspec.lock
|
||||
|
||||
**/AppFlowy-Collab/
|
||||
|
||||
# ignore the deb filegit
|
||||
# ignore generated assets
|
||||
frontend/package
|
||||
frontend/*.deb
|
||||
|
||||
|
||||
@@ -1,4 +1,93 @@
|
||||
# Release Notes
|
||||
## Version 0.4.8 - 02/13/2024
|
||||
### Bug Fixes
|
||||
- Fixed a possible error when loading workspaces
|
||||
|
||||
## Version 0.4.6 - 02/03/2024
|
||||
### Bug Fixes
|
||||
- Fixed refresh token bug
|
||||
|
||||
## Version 0.4.5 - 02/01/2024
|
||||
### Bug Fixes
|
||||
- Fixed WebSocket connection issue
|
||||
|
||||
## Version 0.4.4 - 01/31/2024
|
||||
### New Features
|
||||
- Added functionality for uploading images to cloud storage.
|
||||
- Enabled anonymous sign-in option for mobile platform users.
|
||||
- Introduced the ability to customize cloud settings directly from the startup page.
|
||||
- Added support for inserting reminders on the mobile platform.
|
||||
- Overhauled the user interface on mobile devices, including improvements to the action bottom sheet, editor toolbar, database details page, and app bar.
|
||||
- Implemented a shortcut (F2 key) to rename the current view.
|
||||
|
||||
### Bug Fixes
|
||||
- Fixed an issue where the font family was not displaying correctly on the mobile platform.
|
||||
- Resolved a problem with the mobile row detail title not updating correctly.
|
||||
- Fixed issues related to deleting images and refactored the image actions menu for better usability.
|
||||
- Fixed other known issues.
|
||||
|
||||
# Release Notes
|
||||
## Version 0.4.3 - 01/16/2024
|
||||
### Bug Fixes
|
||||
- Fixed file name too long issue
|
||||
|
||||
## Version 0.4.2 - 01/15/2024
|
||||
AppFlowy for Android is available to download on GitHub.
|
||||
If you’ve been using our desktop app, it’s important to read [this guide](https://docs.appflowy.io/docs/guides/sync-desktop-and-mobile) before logging into the mobile app.
|
||||
### New Features
|
||||
- Enhanced RTL (Right-to-Left) support for mobile platforms.
|
||||
- Optimized selection gesture system on mobile.
|
||||
- Optimized the mobile toolbar menu.
|
||||
- Improved reference menu (‘@’ menu).
|
||||
- Updated privacy policy.
|
||||
- Improved the data import process for AppFlowy by implementing a progress indicator and compressing the data to enhance efficiency.
|
||||
- Enhanced the utilization of local disk space to optimize storage consumption.
|
||||
### Bug Fixes
|
||||
- Fixed sign-in cancellation issue on mobile.
|
||||
- Resolved keyboard close bug on Android.
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
@@ -12,8 +12,11 @@
|
||||
"program": "./lib/main.dart",
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug",
|
||||
"RUST_LOG": "trace",
|
||||
"RUST_BACKTRACE": "1"
|
||||
},
|
||||
// uncomment the following line to testing performance.
|
||||
// "flutterMode": "profile",
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
{
|
||||
@@ -25,7 +28,7 @@
|
||||
"preLaunchTask": "AF: Build Appflowy Core",
|
||||
"env": {
|
||||
"RUST_LOG": "trace",
|
||||
"RUST_BACKTRACE": 1
|
||||
"RUST_BACKTRACE": "1"
|
||||
},
|
||||
"cwd": "${workspaceRoot}/appflowy_flutter"
|
||||
},
|
||||
@@ -44,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",
|
||||
@@ -55,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",
|
||||
@@ -66,6 +91,17 @@
|
||||
},
|
||||
"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",
|
||||
|
||||
@@ -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",
|
||||
@@ -84,25 +116,6 @@
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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: Flutter Package Get",
|
||||
"AF: Generate Language Files",
|
||||
"AF: Generate Freezed Files",
|
||||
"AF: Generate Svg Files"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "new"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Build Appflowy Core For Android",
|
||||
"type": "shell",
|
||||
@@ -112,23 +125,6 @@
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"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: Code Gen",
|
||||
"type": "shell",
|
||||
@@ -136,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"
|
||||
@@ -150,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",
|
||||
@@ -166,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",
|
||||
@@ -237,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",
|
||||
@@ -274,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"
|
||||
}
|
||||
@@ -293,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}"
|
||||
}
|
||||
@@ -309,18 +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"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "AF: Generate AppFlowyEnv",
|
||||
"type": "shell",
|
||||
"command": "dart run build_runner build --delete-conflicting-outputs",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/appflowy_flutter/packages/appflowy_backend"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ extend = [
|
||||
{ path = "scripts/makefile/flutter.toml" },
|
||||
{ path = "scripts/makefile/tool.toml" },
|
||||
{ path = "scripts/makefile/tauri.toml" },
|
||||
{ path = "scripts/makefile/web.toml" },
|
||||
]
|
||||
|
||||
[config]
|
||||
@@ -25,9 +26,10 @@ 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.7"
|
||||
APPFLOWY_VERSION = "0.4.8"
|
||||
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
|
||||
@@ -47,6 +49,8 @@ LIB_EXT = "a"
|
||||
APP_ENVIRONMENT = "local"
|
||||
FLUTTER_FLOWY_SDK_PATH = "appflowy_flutter/packages/appflowy_backend"
|
||||
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
|
||||
WEB_BACKEND_SERVICE_PATH = "appflowy_web/src/services/backend"
|
||||
WEB_LIB_PATH= "appflowy_web/wasm-libs/af-wasm"
|
||||
# Test default config
|
||||
TEST_CRATE_TYPE = "cdylib"
|
||||
TEST_LIB_EXT = "dylib"
|
||||
@@ -126,6 +130,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]
|
||||
@@ -200,6 +205,7 @@ 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]
|
||||
@@ -207,6 +213,7 @@ BUILD_FLAG = "release"
|
||||
TARGET_OS = "android"
|
||||
CRATE_TYPE = "cdylib"
|
||||
FLUTTER_OUTPUT_DIR = "Release"
|
||||
PRODUCT_EXT = "apk"
|
||||
LIB_EXT = "so"
|
||||
|
||||
[tasks.echo_env]
|
||||
@@ -221,6 +228,7 @@ script = ['''
|
||||
echo APP_ENVIRONMENT: ${APP_ENVIRONMENT}
|
||||
echo ${platforms}
|
||||
echo ${BUILD_ARCHS}
|
||||
echo ${BUILD_VERSION}
|
||||
''']
|
||||
script_runner = "@shell"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,10 +30,27 @@ linter:
|
||||
rules:
|
||||
- require_trailing_commas
|
||||
|
||||
- prefer_collection_literals
|
||||
- prefer_final_fields
|
||||
- prefer_final_in_for_each
|
||||
- prefer_final_locals
|
||||
|
||||
- sized_box_for_whitespace
|
||||
- use_decorated_box
|
||||
|
||||
- unnecessary_parenthesis
|
||||
- unnecessary_await_in_return
|
||||
- unnecessary_raw_strings
|
||||
|
||||
- avoid_unnecessary_containers
|
||||
- avoid_redundant_argument_values
|
||||
- avoid_unused_constructor_parameters
|
||||
|
||||
- always_declare_return_types
|
||||
|
||||
- sort_constructors_first
|
||||
- unawaited_futures
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
|
||||
|
||||
@@ -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 33
|
||||
compileSdkVersion 34
|
||||
ndkVersion "24.0.8215888"
|
||||
|
||||
compileOptions {
|
||||
@@ -46,8 +52,8 @@ android {
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId "io.appflowy.appflowy"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 31
|
||||
minSdkVersion 29
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
multiDexEnabled true
|
||||
@@ -58,13 +64,18 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
android:name="${applicationName}">
|
||||
<activity android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
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">
|
||||
@@ -45,4 +45,11 @@
|
||||
-->
|
||||
<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>
|
||||
@@ -2,6 +2,9 @@ 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
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int64_t init_sdk(char *path);
|
||||
int64_t init_sdk(char *data);
|
||||
|
||||
void async_event(int64_t port, const uint8_t *input, uintptr_t len);
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
@@ -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.
|
||||
@@ -73,9 +73,9 @@
|
||||
"attributes": { "subtype": "checkbox", "checkbox": true },
|
||||
"delta": [
|
||||
{ "insert": "Click " },
|
||||
{ "insert": "+ New Page ", "attributes": { "code": true } },
|
||||
{ "insert": "+ New Page", "attributes": { "code": true } },
|
||||
{
|
||||
"insert": "button at the bottom of your sidebar to add a new page."
|
||||
"insert": " button at the bottom of your sidebar to add a new page."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,23 +1 @@
|
||||
# Initial Setup
|
||||
# 1. Copy the dev.env file to .env:
|
||||
# cp dev.env .env
|
||||
# 2. Alternatively, you can generate the .env file using the "Generate Env File" task in VSCode.
|
||||
|
||||
# Configuring Cloud Type
|
||||
# This configuration file is used to specify the cloud type and the necessary configurations for each cloud type. The available options are:
|
||||
# Local: 0
|
||||
# Supabase: 1
|
||||
# AppFlowy Cloud: 2
|
||||
|
||||
CLOUD_TYPE=0
|
||||
|
||||
# Supabase Configuration
|
||||
# If you're using Supabase (CLOUD_TYPE=1), you need to provide the following configurations:
|
||||
SUPABASE_URL=replace-with-your-supabase-url
|
||||
SUPABASE_ANON_KEY=replace-with-your-supabase-key
|
||||
|
||||
# AppFlowy Cloud Configuration
|
||||
# If you're using AppFlowy Cloud (CLOUD_TYPE=2), you need to provide the following configurations:
|
||||
APPFLOWY_CLOUD_BASE_URL=replace-with-your-appflowy-cloud-url
|
||||
APPFLOWY_CLOUD_WS_BASE_URL=replace-with-your-appflowy-cloud-ws-url
|
||||
APPFLOWY_CLOUD_GOTRUE_URL=replace-with-your-appflowy-cloud-gotrue-url
|
||||
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);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.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';
|
||||
@@ -14,62 +15,63 @@ const defaultLastCardName = 'Card 3';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board add row test', () {
|
||||
testWidgets('Add card from header', (tester) async {
|
||||
group('board add row test:', () {
|
||||
testWidgets('from header', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findFirstCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(FlowyText),
|
||||
matching: find.byType(Text),
|
||||
);
|
||||
|
||||
FlowyText firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.text, defaultFirstCardName);
|
||||
Text firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.data, defaultFirstCardName);
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(AppFlowyGroupHeader),
|
||||
matching: find.byType(FlowySvg),
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svg == FlowySvgs.add_s,
|
||||
),
|
||||
)
|
||||
.first,
|
||||
.at(1),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(IntrinsicHeight),
|
||||
of: find.byType(RowCardContainer),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.text, newCardName);
|
||||
expect(firstCardText.data, newCardName);
|
||||
});
|
||||
|
||||
testWidgets('Add card from footer', (tester) async {
|
||||
testWidgets('from footer', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Board);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findLastCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(FlowyText),
|
||||
matching: find.byType(Text),
|
||||
);
|
||||
|
||||
FlowyText? lastCardText =
|
||||
tester.widgetList(findLastCard).last as FlowyText;
|
||||
expect(lastCardText.text, defaultLastCardName);
|
||||
Text? lastCardText = tester.widgetList(findLastCard).last as Text;
|
||||
expect(lastCardText.data, defaultLastCardName);
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
@@ -77,25 +79,25 @@ void main() {
|
||||
of: find.byType(AppFlowyGroupFooter),
|
||||
matching: find.byType(FlowySvg),
|
||||
)
|
||||
.first,
|
||||
.at(1),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(IntrinsicHeight),
|
||||
of: find.byType(RowCardContainer),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
lastCardText = tester.widgetList(findLastCard).last as FlowyText;
|
||||
expect(lastCardText.text, newCardName);
|
||||
lastCardText = tester.widgetList(findLastCard).last as Text;
|
||||
expect(lastCardText.data, newCardName);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_property.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:flutter/widgets.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.text(card1Name),
|
||||
matching: find.byType(AppFlowyGroupCard),
|
||||
);
|
||||
final doingGroup = find.text('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(Text),
|
||||
),
|
||||
);
|
||||
expect(card1StatusFinder, findsNWidgets(1));
|
||||
final card1StatusText = tester.widget<Text>(card1StatusFinder).data;
|
||||
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,36 +18,87 @@ 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);
|
||||
final card1 = find.text(name);
|
||||
await tester.hoverOnWidget(
|
||||
card1,
|
||||
onHover: () async {
|
||||
final moreOption = find.byType(CardMoreOption);
|
||||
final moreOption = find.byType(MoreCardOptionsAccessory);
|
||||
await tester.tapButton(moreOption);
|
||||
},
|
||||
);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
expect(find.findTextInFlowyText(name), findsNothing);
|
||||
expect(find.text(name), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('duplicate item in ToDo card', (tester) async {
|
||||
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);
|
||||
final card1 = find.text(name);
|
||||
await tester.hoverOnWidget(
|
||||
card1,
|
||||
onHover: () async {
|
||||
final moreOption = find.byType(CardMoreOption);
|
||||
final moreOption = find.byType(MoreCardOptionsAccessory);
|
||||
await tester.tapButton(moreOption);
|
||||
},
|
||||
);
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -2,6 +2,7 @@ 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();
|
||||
@@ -9,4 +10,5 @@ void startTesting() {
|
||||
// Board integration tests
|
||||
board_row_test.main();
|
||||
board_add_row_test.main();
|
||||
board_group_test.main();
|
||||
}
|
||||
@@ -32,8 +32,8 @@ void main() {
|
||||
const service = TestWorkspaceService(TestWorkspace.board);
|
||||
|
||||
group('board', () {
|
||||
setUpAll(() async => await service.setUpAll());
|
||||
setUp(() async => await service.setUp());
|
||||
setUpAll(() async => service.setUpAll());
|
||||
setUp(() async => service.setUp());
|
||||
|
||||
testWidgets('open the board with data structure in v0.2.0', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,23 +1,37 @@
|
||||
// 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('auth', () {
|
||||
testWidgets('sign in with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
group('appflowy cloud auth', () {
|
||||
testWidgets('sign in', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
});
|
||||
|
||||
testWidgets('sign out with supabase', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
testWidgets('sign out', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// Open the setting page and sign out
|
||||
@@ -34,52 +48,37 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('sign in as annoymous', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
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.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);
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
testWidgets('enable sync', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
|
||||
await tester.tapGoogleLoginInButton();
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.syncSetting);
|
||||
await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// the switch should be on by default
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync();
|
||||
tester.assertAppFlowyCloudEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync(AppFlowyCloudEnableSync);
|
||||
|
||||
// the switch should be off
|
||||
tester.assertEnableSyncSwitchValue(false);
|
||||
tester.assertAppFlowyCloudEnableSyncSwitchValue(false);
|
||||
|
||||
// the switch should be on after toggling
|
||||
await tester.toggleEnableSync();
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
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,70 @@
|
||||
// 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();
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.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';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -14,7 +14,9 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
|
||||
// open setting
|
||||
await tester.tapDatabaseSettingButton();
|
||||
@@ -36,7 +38,7 @@ void main() {
|
||||
|
||||
// Create calendar view
|
||||
const name = 'calendar';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: ViewLayoutPB.Calendar,
|
||||
);
|
||||
@@ -68,16 +70,18 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create the calendar view
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
|
||||
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
|
||||
await tester.tapAddCalendarEventButton();
|
||||
await tester.hoverOnTodayCalendarCell(
|
||||
// Tap on create new event button
|
||||
onHover: () async => tester.tapAddCalendarEventButton(),
|
||||
);
|
||||
|
||||
// Make sure that the event editor popup is shown
|
||||
tester.assertEventEditorOpen();
|
||||
@@ -90,15 +94,9 @@ void main() {
|
||||
// Double click on today's calendar cell to create a new event
|
||||
await tester.doubleClickCalendarCell(DateTime.now());
|
||||
|
||||
// Make sure that the event editor popup is shown
|
||||
tester.assertEventEditorOpen();
|
||||
|
||||
// Make sure that the event is inserted in the cell
|
||||
tester.assertNumberOfEventsInCalendar(2);
|
||||
|
||||
// Dismiss the event editor popup
|
||||
await tester.dismissEventEditor();
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
tester.assertEventEditorOpen();
|
||||
@@ -112,7 +110,7 @@ void main() {
|
||||
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
||||
|
||||
// Click on the event
|
||||
await tester.openCalendarEvent(index: 1);
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
tester.assertEventEditorOpen();
|
||||
|
||||
// Click on the open icon
|
||||
@@ -137,7 +135,7 @@ void main() {
|
||||
tester.assertNumberOfEventsOnSpecificDay(2, DateTime.now());
|
||||
|
||||
// Delete event from row detail page
|
||||
await tester.openCalendarEvent(index: 1);
|
||||
await tester.openCalendarEvent(index: 0);
|
||||
await tester.openEventToRowDetailPage();
|
||||
tester.assertRowDetailPageOpened();
|
||||
|
||||
@@ -154,16 +152,18 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create the calendar view
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Calendar);
|
||||
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);
|
||||
final firstOfThisMonth = DateTime(today.year, today.month);
|
||||
await tester.doubleClickCalendarCell(firstOfThisMonth);
|
||||
await tester.dismissEventEditor();
|
||||
|
||||
// Drag and drop the event onto the next week, same day
|
||||
await tester.dragDropRescheduleCalendarEvent(firstOfThisMonth);
|
||||
await tester.dragDropRescheduleCalendarEvent();
|
||||
|
||||
// Make sure that the event has been rescheduled to the new date
|
||||
final sameDayNextWeek = firstOfThisMonth.add(const Duration(days: 7));
|
||||
@@ -1,21 +1,21 @@
|
||||
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_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';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid cell', () {
|
||||
testWidgets('edit text cell', (tester) async {
|
||||
group('edit grid cell:', () {
|
||||
testWidgets('text', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
@@ -23,7 +23,7 @@ void main() {
|
||||
input: 'hello world',
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'hello world',
|
||||
@@ -32,12 +32,12 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
// Makesure the text cells are filled with the right content when there are
|
||||
// Make sure the text cells are filled with the right content when there are
|
||||
// multiple text cell
|
||||
testWidgets('edit multiple text cells', (tester) async {
|
||||
testWidgets('multiple text cells', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'my grid',
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
@@ -56,14 +56,13 @@ void main() {
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'hello',
|
||||
cellIndex: 0,
|
||||
);
|
||||
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: 'world',
|
||||
@@ -73,11 +72,11 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit number cell', (tester) async {
|
||||
testWidgets('number', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Number;
|
||||
|
||||
@@ -96,7 +95,7 @@ void main() {
|
||||
input: '0.2',
|
||||
);
|
||||
// -1 -> -1
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: fieldType,
|
||||
content: '-1',
|
||||
@@ -109,7 +108,7 @@ void main() {
|
||||
input: '.1',
|
||||
);
|
||||
// 0.2 -> 0.2
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 1,
|
||||
fieldType: fieldType,
|
||||
content: '0.2',
|
||||
@@ -122,7 +121,7 @@ void main() {
|
||||
input: '',
|
||||
);
|
||||
// .1 -> 0.1
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 2,
|
||||
fieldType: fieldType,
|
||||
content: '0.1',
|
||||
@@ -131,11 +130,11 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit checkbox cell', (tester) async {
|
||||
testWidgets('checkbox', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.assertCheckboxCell(rowIndex: 0, isSelected: false);
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 0);
|
||||
@@ -149,11 +148,11 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit create time cell', (tester) async {
|
||||
testWidgets('created time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.CreatedTime;
|
||||
// Create a create time field
|
||||
@@ -167,11 +166,11 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit last time cell', (tester) async {
|
||||
testWidgets('last modified time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.LastEditedTime;
|
||||
// Create a last time field
|
||||
@@ -185,11 +184,11 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit date time cell', (tester) async {
|
||||
testWidgets('date time', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.DateTime;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
@@ -216,13 +215,13 @@ void main() {
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
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();
|
||||
@@ -230,8 +229,9 @@ void main() {
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: DateFormat('MMM dd, y HH:mm').format(now),
|
||||
);
|
||||
|
||||
@@ -239,12 +239,14 @@ void main() {
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Change date format
|
||||
await tester.tapChangeDateTimeFormatButton();
|
||||
await tester.changeDateFormat();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: DateFormat('dd/MM/y HH:mm').format(now),
|
||||
);
|
||||
|
||||
@@ -252,12 +254,14 @@ void main() {
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Change time format
|
||||
await tester.tapChangeDateTimeFormatButton();
|
||||
await tester.changeTimeFormat();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: DateFormat('dd/MM/y hh:mm a').format(now),
|
||||
);
|
||||
|
||||
@@ -267,22 +271,23 @@ void main() {
|
||||
// Clear the date and time
|
||||
await tester.clearDate();
|
||||
|
||||
await tester.assertDateCellInGrid(
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: '',
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit single select cell', (tester) async {
|
||||
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.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Tap the cell to invoke the selection option editor
|
||||
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
@@ -347,7 +352,7 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit multi select cell', (tester) async {
|
||||
testWidgets('multi select', (tester) async {
|
||||
final tags = [
|
||||
'tag 1',
|
||||
'tag 2',
|
||||
@@ -358,7 +363,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.MultiSelect;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
@@ -436,107 +441,107 @@ void main() {
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('edit checklist cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
testWidgets('checklist', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
const fieldType = FieldType.Checklist;
|
||||
await tester.createField(fieldType, fieldType.name);
|
||||
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);
|
||||
// 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);
|
||||
// tap on the first checklist cell
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// assert that the checklist editor is shown
|
||||
tester.assertChecklistEditorVisible(visible: true);
|
||||
// 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);
|
||||
// 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,
|
||||
);
|
||||
// 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");
|
||||
// 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,
|
||||
);
|
||||
// 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 new task editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// dismiss checklist cell 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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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,
|
||||
);
|
||||
// assert that the task was inserted
|
||||
tester.assertChecklistTaskInEditor(
|
||||
index: 1,
|
||||
name: "task 2",
|
||||
isChecked: false,
|
||||
);
|
||||
|
||||
// mark it as complete
|
||||
await tester.checkChecklistTask(index: 1);
|
||||
// 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,
|
||||
);
|
||||
// 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();
|
||||
// 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);
|
||||
// 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);
|
||||
// re-open the cell editor
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// hover over first task and delete it
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
// hover over first task and delete it
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
|
||||
// dismiss cell editor
|
||||
await tester.dismissCellEditor();
|
||||
// dismiss cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// assert that progressbar is shown in grid at 100%
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: 1);
|
||||
// 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);
|
||||
// re-open the cell edior
|
||||
await tester.tapChecklistCellInGrid(rowIndex: 0);
|
||||
|
||||
// delete the remaining task
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
// delete the remaining task
|
||||
await tester.deleteChecklistTask(index: 0);
|
||||
|
||||
// dismiss the cell editor
|
||||
await tester.dismissCellEditor();
|
||||
// dismiss the cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// check that the progress bar is not viisble
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
// check that the progress bar is not viisble
|
||||
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pbenum.dart';
|
||||
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';
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -15,8 +15,8 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
@@ -27,29 +27,29 @@ void main() {
|
||||
// hide the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapHidePropertyButton();
|
||||
await tester.noFieldWithName('New field 1');
|
||||
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');
|
||||
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');
|
||||
await tester.tapTabBarLinkedViewByViewName('Grid');
|
||||
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');
|
||||
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');
|
||||
tester.noFieldWithName('New field 1');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
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/util/field_type_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.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 'package:intl/intl.dart';
|
||||
|
||||
import '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid field editor:', () {
|
||||
testWidgets('rename existing field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Name');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
await tester.renameField('hello world');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
await tester.tapGridFieldWithName('hello world');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('update field type of existing field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.changeFieldTypeOfFieldWithName('Type', FieldType.Checkbox);
|
||||
|
||||
await tester.assertFieldTypeWithFieldName(
|
||||
'Type',
|
||||
FieldType.Checkbox,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create a field and rename it', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checklist, 'checklist');
|
||||
|
||||
// check the field is created successfully
|
||||
tester.findFieldWithName('checklist');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('delete field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checkbox, 'New field 1');
|
||||
|
||||
// Delete the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapDeletePropertyButton();
|
||||
|
||||
// confirm delete
|
||||
await tester.tapDialogOkButton();
|
||||
|
||||
tester.noFieldWithName('New field 1');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('duplicate field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField('New field 1');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// duplicate the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapDuplicatePropertyButton();
|
||||
|
||||
tester.findFieldWithName('New field 1 (copy)');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('insert field on either side of a field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
// insert new field to the right
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapInsertFieldButton(left: false, name: 'Right');
|
||||
await tester.dismissFieldEditor();
|
||||
tester.findFieldWithName('Right');
|
||||
|
||||
// insert new field to the right
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapInsertFieldButton(left: true, name: "Left");
|
||||
await tester.dismissFieldEditor();
|
||||
tester.findFieldWithName('Left');
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create checklist field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
|
||||
// Open the type option menu
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
|
||||
await tester.selectFieldType(FieldType.Checklist);
|
||||
|
||||
// After update the field type, the cells should be updated
|
||||
await tester.findCellByFieldType(FieldType.Checklist);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create list of fields', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
for (final fieldType in [
|
||||
FieldType.Checklist,
|
||||
FieldType.DateTime,
|
||||
FieldType.Number,
|
||||
FieldType.URL,
|
||||
FieldType.MultiSelect,
|
||||
FieldType.LastEditedTime,
|
||||
FieldType.CreatedTime,
|
||||
FieldType.Checkbox,
|
||||
]) {
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField(fieldType.name);
|
||||
|
||||
// Open the type option menu
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
|
||||
await tester.selectFieldType(fieldType);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// After update the field type, the cells should be updated
|
||||
await tester.findCellByFieldType(fieldType);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('field types with empty type option editor', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
for (final fieldType in [
|
||||
FieldType.RichText,
|
||||
FieldType.Checkbox,
|
||||
FieldType.Checklist,
|
||||
FieldType.URL,
|
||||
]) {
|
||||
// create the field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField(fieldType.i18n);
|
||||
|
||||
// change field type
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(fieldType);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// open the field editor
|
||||
await tester.tapGridFieldWithName(fieldType.i18n);
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// check type option editor is empty
|
||||
tester.expectEmptyTypeOptionEditor();
|
||||
await tester.dismissFieldEditor();
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('number field type option', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
// create a number field
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField("Number");
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.Number);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// enter some data into the first number cell
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.Number,
|
||||
input: '123',
|
||||
);
|
||||
// edit the next cell to force the previous cell at row 0 to lose focus
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.Number,
|
||||
input: '0.2',
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.Number,
|
||||
content: '123',
|
||||
);
|
||||
|
||||
// open editor and change number format
|
||||
await tester.tapGridFieldWithName('Number');
|
||||
await tester.tapEditFieldButton();
|
||||
await tester.changeNumberFieldFormat();
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// assert number format has been changed
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.Number,
|
||||
content: '\$123',
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('add option', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
|
||||
// invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// tap 'add option' button
|
||||
await tester.tapAddSelectOptionButton();
|
||||
const text = 'Hello AppFlowy';
|
||||
final inputField = find.descendant(
|
||||
of: find.byType(CreateOptionTextField),
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await tester.enterText(inputField, text);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||
|
||||
// check the result
|
||||
tester.expectToSeeText(text);
|
||||
});
|
||||
|
||||
testWidgets('date time field type options', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
// create a date field
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField(FieldType.DateTime.i18n);
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.DateTime);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// edit the first date cell
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.toggleIncludeTime();
|
||||
final now = DateTime.now();
|
||||
await tester.selectDay(content: now.day);
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: DateFormat('MMM dd, y HH:mm').format(now),
|
||||
);
|
||||
|
||||
// open editor and change date & time format
|
||||
await tester.tapGridFieldWithName(FieldType.DateTime.i18n);
|
||||
await tester.tapEditFieldButton();
|
||||
await tester.changeDateFormat();
|
||||
await tester.changeTimeFormat();
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// assert date format has been changed
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: DateFormat('dd/MM/y hh:mm a').format(now),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('last modified and created at field type options',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
final created = DateTime.now();
|
||||
|
||||
// create a created at field
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField(FieldType.CreatedTime.i18n);
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.CreatedTime);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// create a last modified field
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField(FieldType.LastEditedTime.i18n);
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.LastEditedTime);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
final modified = DateTime.now();
|
||||
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.CreatedTime,
|
||||
content: DateFormat('MMM dd, y HH:mm').format(created),
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.LastEditedTime,
|
||||
content: DateFormat('MMM dd, y HH:mm').format(modified),
|
||||
);
|
||||
|
||||
// open field editor and change date & time format
|
||||
await tester.tapGridFieldWithName(FieldType.LastEditedTime.i18n);
|
||||
await tester.tapEditFieldButton();
|
||||
await tester.changeDateFormat();
|
||||
await tester.changeTimeFormat();
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// open field editor and change date & time format
|
||||
await tester.tapGridFieldWithName(FieldType.CreatedTime.i18n);
|
||||
await tester.tapEditFieldButton();
|
||||
await tester.changeDateFormat();
|
||||
await tester.changeTimeFormat();
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// assert format has been changed
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.CreatedTime,
|
||||
content: DateFormat('dd/MM/y hh:mm a').format(created),
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.LastEditedTime,
|
||||
content: DateFormat('dd/MM/y hh:mm a').format(modified),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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,206 @@
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart';
|
||||
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 '../util/database_test_op.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('reminder in database', () {
|
||||
testWidgets('add date field and add reminder', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// Change to date type
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.DateTime);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Open date picker
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Select date
|
||||
final isToday = await tester.selectLastDateInPicker();
|
||||
|
||||
// Select "On day of event" reminder
|
||||
await tester.selectReminderOption(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Expect "On day of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Dismiss the cell/date editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Open date picker again
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Expect "On day of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Dismiss the cell/date editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
int tabIndex = 1;
|
||||
final now = DateTime.now();
|
||||
if (isToday && now.hour >= 9) {
|
||||
tabIndex = 0;
|
||||
}
|
||||
|
||||
// Open "Upcoming" in Notification hub
|
||||
await tester.openNotificationHub(tabIndex: tabIndex);
|
||||
|
||||
// Expect 1 notification
|
||||
tester.expectNotificationItems(1);
|
||||
});
|
||||
|
||||
testWidgets('navigate from reminder to open row', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// Change to date type
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.DateTime);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Open date picker
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Select date
|
||||
final isToday = await tester.selectLastDateInPicker();
|
||||
|
||||
// Select "On day of event"-reminder
|
||||
await tester.selectReminderOption(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Expect "On day of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Dismiss the cell/date editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Open date picker again
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Expect "On day of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Dismiss the cell/date editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Create and Navigate to a new document
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
int tabIndex = 1;
|
||||
final now = DateTime.now();
|
||||
if (isToday && now.hour >= 9) {
|
||||
tabIndex = 0;
|
||||
}
|
||||
|
||||
// Open correct tab in Notification hub
|
||||
await tester.openNotificationHub(tabIndex: tabIndex);
|
||||
|
||||
// Expect 1 notification
|
||||
tester.expectNotificationItems(1);
|
||||
|
||||
// Tap on the notification
|
||||
await tester.tap(find.byType(NotificationItem));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect to see Row Editor Dialog
|
||||
tester.expectToSeeRowDetailsPageDialog();
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'toggle include time sets reminder option correctly',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// Change to date type
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.DateTime);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Open date picker
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Select date
|
||||
await tester.selectLastDateInPicker();
|
||||
|
||||
// Select "On day of event"-reminder
|
||||
await tester.selectReminderOption(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Expect "On day of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Dismiss the cell/date editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Open date picker again
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Expect "On day of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.onDayOfEvent);
|
||||
|
||||
// Toggle include time on
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
// Expect "At time of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.atTimeOfEvent);
|
||||
|
||||
// Dismiss the cell/date editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Open date picker again
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.findDateEditor(findsOneWidget);
|
||||
|
||||
// Expect "At time of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.atTimeOfEvent);
|
||||
|
||||
// Select "One hour before"-reminder
|
||||
await tester.selectReminderOption(ReminderOption.oneHourBefore);
|
||||
|
||||
// Expect "One hour before" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.oneHourBefore);
|
||||
|
||||
// Toggle include time off
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
// Expect "On day of event" to be displayed
|
||||
tester.expectSelectedReminder(ReminderOption.onDayOfEvent);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,40 +1,43 @@
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
|
||||
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-folder2/view.pb.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';
|
||||
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 {
|
||||
group('grid row detail page:', () {
|
||||
testWidgets('opens', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.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();
|
||||
|
||||
// Each row detail page should have a document
|
||||
await tester.assertDocumentExistInRowDetailPage();
|
||||
});
|
||||
|
||||
testWidgets('insert emoji in the row detail page', (tester) async {
|
||||
testWidgets('add emoji', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
@@ -42,30 +45,27 @@ void main() {
|
||||
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 {
|
||||
testWidgets('update emoji', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.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.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
|
||||
@@ -78,18 +78,17 @@ void main() {
|
||||
expect(emojiText, findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('remove emoji in the row detail page', (tester) async {
|
||||
testWidgets('remove emoji', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.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.switchToEmojiList();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Remove the emoji
|
||||
@@ -100,12 +99,12 @@ void main() {
|
||||
expect(emojiText, findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('create list of fields in row detail page', (tester) async {
|
||||
testWidgets('create list of fields', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
@@ -124,10 +123,9 @@ void main() {
|
||||
await tester.renameField(fieldType.name);
|
||||
|
||||
// Open the type option menu
|
||||
await tester.tapTypeOptionButton();
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
|
||||
await tester.selectFieldType(fieldType);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// After update the field type, the cells should be updated
|
||||
await tester.findCellByFieldType(fieldType);
|
||||
@@ -140,7 +138,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
@@ -173,7 +171,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
@@ -216,27 +214,13 @@ void main() {
|
||||
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.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 contents 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);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
@@ -276,7 +260,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
@@ -310,12 +294,12 @@ void main() {
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('delete row in row detail page', (tester) async {
|
||||
testWidgets('delete row', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
@@ -327,12 +311,12 @@ void main() {
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
});
|
||||
|
||||
testWidgets('duplicate row in row detail page', (tester) async {
|
||||
testWidgets('duplicate row', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
@@ -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();
|
||||
@@ -16,7 +16,7 @@ void main() {
|
||||
// check the text cell
|
||||
final textCells = <String>['A', 'B', 'C', 'D', 'E', '', '', '', '', ''];
|
||||
for (final (index, content) in textCells.indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.RichText,
|
||||
content: content,
|
||||
@@ -34,7 +34,7 @@ void main() {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
];
|
||||
for (final (index, content) in checkboxCells.indexed) {
|
||||
await tester.assertCheckboxCell(
|
||||
@@ -54,10 +54,10 @@ void main() {
|
||||
'10',
|
||||
'11',
|
||||
'12',
|
||||
''
|
||||
'',
|
||||
];
|
||||
for (final (index, content) in numberCells.indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.Number,
|
||||
content: content,
|
||||
@@ -74,7 +74,7 @@ void main() {
|
||||
'',
|
||||
];
|
||||
for (final (index, content) in urlCells.indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.URL,
|
||||
content: content,
|
||||
@@ -135,7 +135,7 @@ void main() {
|
||||
null,
|
||||
];
|
||||
for (final (index, percent) in checklistCells.indexed) {
|
||||
await tester.assertChecklistCellInGrid(
|
||||
tester.assertChecklistCellInGrid(
|
||||
rowIndex: index,
|
||||
percent: percent,
|
||||
);
|
||||
@@ -152,11 +152,12 @@ void main() {
|
||||
'Jun 16, 2023',
|
||||
'',
|
||||
'',
|
||||
''
|
||||
'',
|
||||
];
|
||||
for (final (index, content) in dateCells.indexed) {
|
||||
await tester.assertDateCellInGrid(
|
||||
tester.assertCellContent(
|
||||
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();
|
||||
@@ -28,7 +28,7 @@ void main() {
|
||||
'',
|
||||
];
|
||||
for (final (index, content) in textCells.indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.RichText,
|
||||
content: content,
|
||||
@@ -51,7 +51,7 @@ void main() {
|
||||
'',
|
||||
'',
|
||||
].indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.RichText,
|
||||
content: content,
|
||||
@@ -75,7 +75,7 @@ void main() {
|
||||
'',
|
||||
'',
|
||||
].indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.RichText,
|
||||
content: content,
|
||||
@@ -86,7 +86,7 @@ void main() {
|
||||
|
||||
testWidgets('add checkbox sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
// create a filter
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Checkbox, 'Done');
|
||||
|
||||
@@ -136,7 +136,7 @@ void main() {
|
||||
|
||||
testWidgets('add number sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
// create a filter
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Number, 'number');
|
||||
|
||||
@@ -153,7 +153,7 @@ void main() {
|
||||
'12',
|
||||
'',
|
||||
].indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.Number,
|
||||
content: content,
|
||||
@@ -176,7 +176,7 @@ void main() {
|
||||
'-2',
|
||||
'',
|
||||
].indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.Number,
|
||||
content: content,
|
||||
@@ -188,7 +188,7 @@ void main() {
|
||||
|
||||
testWidgets('add checkbox and number sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
// create a filter
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Checkbox, 'Done');
|
||||
|
||||
@@ -255,7 +255,7 @@ void main() {
|
||||
'2',
|
||||
'',
|
||||
].indexed) {
|
||||
await tester.assertCellContent(
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.Number,
|
||||
content: content,
|
||||
@@ -264,5 +264,111 @@ void main() {
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('reorder sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Checkbox, 'Done');
|
||||
|
||||
// open the sort menu and sort checkbox by descending
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapSortButtonByName('Done');
|
||||
await tester.tapSortByDescending();
|
||||
|
||||
// add another sort, this time by number descending
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapCreateSortByFieldTypeInSortMenu(
|
||||
FieldType.Number,
|
||||
'number',
|
||||
);
|
||||
await tester.tapSortButtonByName('number');
|
||||
await tester.tapSortByDescending();
|
||||
|
||||
// check checkbox cell order
|
||||
for (final (index, content) in <bool>[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
].indexed) {
|
||||
await tester.assertCheckboxCell(
|
||||
rowIndex: index,
|
||||
isSelected: content,
|
||||
);
|
||||
}
|
||||
|
||||
// check number cell order
|
||||
for (final (index, content) in <String>[
|
||||
'1',
|
||||
'0.2',
|
||||
'0.1',
|
||||
'-1',
|
||||
'-2',
|
||||
'12',
|
||||
'11',
|
||||
'10',
|
||||
'2',
|
||||
'',
|
||||
].indexed) {
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.Number,
|
||||
content: content,
|
||||
);
|
||||
}
|
||||
|
||||
// reorder sort
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.reorderSort(
|
||||
(FieldType.Number, 'number'),
|
||||
(FieldType.Checkbox, 'Done'),
|
||||
);
|
||||
|
||||
// check checkbox cell order
|
||||
for (final (index, content) in <bool>[
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
].indexed) {
|
||||
await tester.assertCheckboxCell(
|
||||
rowIndex: index,
|
||||
isSelected: content,
|
||||
);
|
||||
}
|
||||
|
||||
// check the number cell order
|
||||
for (final (index, content) in <String>[
|
||||
'12',
|
||||
'11',
|
||||
'10',
|
||||
'2',
|
||||
'1',
|
||||
'0.2',
|
||||
'0.1',
|
||||
'-1',
|
||||
'-2',
|
||||
'',
|
||||
].indexed) {
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.Number,
|
||||
content: content,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
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: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();
|
||||
@@ -15,18 +14,18 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// Create grid view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.grid);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Grid);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Grid);
|
||||
|
||||
// Create calendar view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.calendar);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Calendar);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Calendar);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@@ -36,10 +35,10 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// rename board view
|
||||
@@ -61,10 +60,10 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Create board view
|
||||
await tester.tapCreateLinkedDatabaseViewButton(AddButtonAction.board);
|
||||
await tester.tapCreateLinkedDatabaseViewButton(DatabaseLayoutPB.Board);
|
||||
tester.assertCurrentDatabaseTagIs(DatabaseLayoutPB.Board);
|
||||
|
||||
// delete the board
|
||||
@@ -1,189 +0,0 @@
|
||||
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_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/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';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid page', () {
|
||||
testWidgets('rename existing field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Name');
|
||||
await tester.tapEditPropertyButton();
|
||||
|
||||
await tester.renameField('hello world');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
await tester.tapGridFieldWithName('hello world');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('update field type of existing field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditPropertyButton();
|
||||
|
||||
await tester.tapTypeOptionButton();
|
||||
await tester.selectFieldType(FieldType.Checkbox);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
await tester.assertFieldTypeWithFieldName(
|
||||
'Type',
|
||||
FieldType.Checkbox,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create a field and rename it', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new grid
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checklist, 'checklist');
|
||||
|
||||
// check the field is created successfully
|
||||
await tester.findFieldWithName('checklist');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('delete field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.createField(FieldType.Checkbox, 'New field 1');
|
||||
|
||||
// Delete the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapDeletePropertyButton();
|
||||
|
||||
// confirm delete
|
||||
await tester.tapDialogOkButton();
|
||||
|
||||
await tester.noFieldWithName('New field 1');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('duplicate field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// create a field
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
await tester.renameField('New field 1');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Delete the field
|
||||
await tester.tapGridFieldWithName('New field 1');
|
||||
await tester.tapDuplicatePropertyButton();
|
||||
|
||||
await tester.findFieldWithName('New field 1 (copy)');
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create checklist field ', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
|
||||
// Open the type option menu
|
||||
await tester.tapTypeOptionButton();
|
||||
|
||||
await tester.selectFieldType(FieldType.Checklist);
|
||||
|
||||
// After update the field type, the cells should be updated
|
||||
await tester.findCellByFieldType(FieldType.Checklist);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create list of fields', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(layout: ViewLayoutPB.Grid);
|
||||
|
||||
for (final fieldType in [
|
||||
FieldType.Checklist,
|
||||
FieldType.DateTime,
|
||||
FieldType.Number,
|
||||
FieldType.URL,
|
||||
FieldType.MultiSelect,
|
||||
FieldType.LastEditedTime,
|
||||
FieldType.CreatedTime,
|
||||
FieldType.Checkbox,
|
||||
]) {
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
await tester.tapNewPropertyButton();
|
||||
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.pumpAndSettle();
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('add option', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditPropertyButton();
|
||||
|
||||
// tap 'add option' button
|
||||
await tester.tapAddSelectOptionButton();
|
||||
const text = 'Hello AppFlowy';
|
||||
final inputField = find.descendant(
|
||||
of: find.byType(CreateOptionTextField),
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await tester.enterText(inputField, text);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
// check the result
|
||||
tester.expectToSeeText(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
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() {
|
||||
@@ -42,5 +45,51 @@ void main() {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -18,7 +18,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// mock the clipboard
|
||||
const lines = 3;
|
||||
|
||||
@@ -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,7 +220,7 @@ void main() {
|
||||
expect(node.delta!.toJson(), [
|
||||
{
|
||||
'insert': text,
|
||||
'attributes': {'href': url}
|
||||
'attributes': {'href': url},
|
||||
}
|
||||
]);
|
||||
},
|
||||
@@ -276,6 +277,22 @@ void main() {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
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 {
|
||||
@@ -290,7 +307,7 @@ extension on WidgetTester {
|
||||
await tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await createNewPageWithName();
|
||||
await createNewPageWithNameUnderParent();
|
||||
|
||||
await beforeTest?.call(editor.getCurrentEditorState());
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// expect to see a new document
|
||||
tester.expectToSeePageName(
|
||||
@@ -35,7 +35,7 @@ void main() {
|
||||
// delete the readme page
|
||||
await tester.hoverOnPageName(
|
||||
gettingStarted,
|
||||
onHover: () async => await tester.tapDeletePageButton(),
|
||||
onHover: () async => tester.tapDeletePageButton(),
|
||||
);
|
||||
|
||||
// the banner should show up and the readme page should be gone
|
||||
@@ -59,7 +59,7 @@ void main() {
|
||||
// delete the readme page
|
||||
await tester.hoverOnPageName(
|
||||
gettingStarted,
|
||||
onHover: () async => await tester.tapDeletePageButton(),
|
||||
onHover: () async => tester.tapDeletePageButton(),
|
||||
);
|
||||
|
||||
// the banner should show up and the readme page should be gone
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
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: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,
|
||||
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();
|
||||
}
|
||||
@@ -32,7 +32,7 @@ void main() {
|
||||
await tester.tapAt(Offset.zero);
|
||||
await tester.tapAt(Offset.zero);
|
||||
|
||||
await tester.createNewPageWithName(name: 'test');
|
||||
await tester.createNewPageWithNameUnderParent(name: 'test');
|
||||
await tester.openPage(gettingStarted);
|
||||
|
||||
// check the status again
|
||||
|
||||
@@ -16,6 +16,8 @@ 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_inline_page_reference_test.dart'
|
||||
as document_inline_page_reference_test;
|
||||
|
||||
void startTesting() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -35,4 +37,5 @@ void startTesting() {
|
||||
document_text_direction_test.main();
|
||||
document_option_action_test.main();
|
||||
document_with_image_block_test.main();
|
||||
document_inline_page_reference_test.main();
|
||||
}
|
||||
@@ -29,8 +29,8 @@ void main() {
|
||||
// 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',
|
||||
'toolbar/text_direction_ltr',
|
||||
'toolbar/text_direction_rtl',
|
||||
];
|
||||
// no text direction items in default/LTR mode
|
||||
var button = find.byWidgetPredicate(
|
||||
|
||||
@@ -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();
|
||||
@@ -39,8 +43,6 @@ void main() {
|
||||
await tester.editor.hoverOnCover();
|
||||
await tester.editor.tapOnChangeCover();
|
||||
await tester.editor.addNetworkImageCover(imageUrl);
|
||||
await tester.editor.switchNetworkImageCover(imageUrl);
|
||||
await tester.editor.dismissCoverPicker();
|
||||
tester.expectToSeeDocumentCover(CoverType.file);
|
||||
|
||||
// Remove the cover
|
||||
@@ -53,15 +55,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 +70,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 +88,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 +107,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,11 +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/database_view/widgets/row/cells/text_cell/text_cell.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/link_to_page_widget.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.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/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';
|
||||
@@ -40,7 +40,7 @@ void main() {
|
||||
Position(path: [1]),
|
||||
),
|
||||
);
|
||||
final gridTextCell = find.byType(GridTextCell).first;
|
||||
final gridTextCell = find.byType(EditableTextCell).first;
|
||||
await tester.tapButton(gridTextCell);
|
||||
|
||||
expect(tester.editor.getCurrentEditorState().selection, isNull);
|
||||
@@ -84,7 +84,7 @@ void main() {
|
||||
|
||||
await createInlineDatabase(tester, ViewLayoutPB.Grid);
|
||||
|
||||
// validate the referenced grid is inserted
|
||||
// validate the inline grid is created
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyEditor),
|
||||
@@ -100,7 +100,7 @@ void main() {
|
||||
|
||||
await createInlineDatabase(tester, ViewLayoutPB.Board);
|
||||
|
||||
// validate the referenced grid is inserted
|
||||
// validate the inline board is created
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyEditor),
|
||||
@@ -116,7 +116,7 @@ void main() {
|
||||
|
||||
await createInlineDatabase(tester, ViewLayoutPB.Calendar);
|
||||
|
||||
// validate the referenced grid is inserted
|
||||
// validate the inline calendar is created
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyEditor),
|
||||
@@ -136,16 +136,14 @@ 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,
|
||||
);
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -155,7 +153,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,
|
||||
@@ -171,10 +169,8 @@ 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,
|
||||
);
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
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/resizeable_image.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-folder2/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide UploadImageMenu;
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart'
|
||||
hide UploadImageMenu, ResizableImage;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -31,9 +35,8 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -61,6 +64,7 @@ void main() {
|
||||
paths: [imagePath],
|
||||
);
|
||||
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
|
||||
);
|
||||
@@ -79,9 +83,8 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -133,9 +136,8 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/button.dart';
|
||||
@@ -20,9 +19,8 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
name: LocaleKeys.document_plugins_createInlineMathEquation.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'math equation',
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -66,9 +64,8 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
name: LocaleKeys.document_plugins_createInlineMathEquation.tr(),
|
||||
layout: ViewLayoutPB.Document,
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'math equation',
|
||||
);
|
||||
|
||||
// tap the first line of the 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,13 +59,13 @@ 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';
|
||||
await tester.hoverOnPageName(
|
||||
pageName,
|
||||
onHover: () async => await tester.renamePage(newName),
|
||||
onHover: () async => tester.renamePage(newName),
|
||||
);
|
||||
final finder = find.descendant(
|
||||
of: find.byType(MentionPageBlock),
|
||||
@@ -78,13 +78,13 @@ 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(
|
||||
pageName,
|
||||
layout: ViewLayoutPB.Grid,
|
||||
onHover: () async => await tester.tapDeletePageButton(),
|
||||
onHover: () async => tester.tapDeletePageButton(),
|
||||
);
|
||||
final finder = find.descendant(
|
||||
of: find.byType(MentionPageBlock),
|
||||
@@ -98,27 +98,30 @@ 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,3 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -24,9 +23,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
@@ -15,9 +14,8 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'outline_test',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -32,9 +30,8 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'outline_test',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -15,14 +14,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,9 +39,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -63,7 +69,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,9 +83,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -88,7 +92,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,9 +120,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -153,9 +155,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -164,14 +164,11 @@ 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(
|
||||
Selection.collapsed(
|
||||
Position(path: [0], offset: 0),
|
||||
),
|
||||
);
|
||||
await tester.editor
|
||||
.updateSelection(Selection.collapsed(Position(path: [0])));
|
||||
await tester.ime.insertCharacter('\n');
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
@@ -187,9 +184,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithName(
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -200,7 +195,7 @@ void main() {
|
||||
|
||||
await tester.editor.updateSelection(
|
||||
Selection.collapsed(
|
||||
Position(path: [0], offset: 0),
|
||||
Position(path: [0]),
|
||||
),
|
||||
);
|
||||
await tester.simulateKeyEvent(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -18,10 +17,7 @@ void main() {
|
||||
|
||||
// create a new document called Sample
|
||||
const pageName = 'Sample';
|
||||
await tester.createNewPageWithName(
|
||||
name: pageName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent(name: pageName);
|
||||
|
||||
// focus on the editor
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -75,10 +71,7 @@ void main() {
|
||||
|
||||
// create a new document called Sample
|
||||
const pageName = 'Sample';
|
||||
await tester.createNewPageWithName(
|
||||
name: pageName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent(name: pageName);
|
||||
|
||||
// focus on the editor
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
@@ -104,7 +97,7 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
const _sample = r'''
|
||||
const _sample = '''
|
||||
# Heading 1
|
||||
## Heading 2
|
||||
### Heading 3
|
||||
@@ -113,9 +106,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
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ void main() {
|
||||
const service = TestWorkspaceService(TestWorkspace.emptyDocument);
|
||||
|
||||
group('Tests on a workspace with only an empty document', () {
|
||||
setUpAll(() async => await service.setUpAll());
|
||||
setUp(() async => await service.setUp());
|
||||
setUpAll(() async => service.setUpAll());
|
||||
setUp(() async => service.setUp());
|
||||
|
||||
testWidgets('/board shortcut creates a new board and view of the board',
|
||||
(tester) async {
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.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('Grid Calculations', () {
|
||||
testWidgets('add calculation and update cell', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Change one Field to Number
|
||||
await tester.changeFieldTypeOfFieldWithName('Type', FieldType.Number);
|
||||
|
||||
expect(find.text('Calculate'), findsOneWidget);
|
||||
|
||||
await tester.changeCalculateAtIndex(1, CalculationType.Sum);
|
||||
|
||||
// Enter values in cells
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.Number,
|
||||
input: '100',
|
||||
);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.Number,
|
||||
input: '100',
|
||||
);
|
||||
|
||||
// Dismiss edit cell
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.enter);
|
||||
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
expect(find.text('200'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('add calculations and remove row', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Change two Fields to Number
|
||||
await tester.changeFieldTypeOfFieldWithName('Type', FieldType.Number);
|
||||
await tester.changeFieldTypeOfFieldWithName('Done', FieldType.Number);
|
||||
|
||||
expect(find.text('Calculate'), findsNWidgets(2));
|
||||
|
||||
await tester.changeCalculateAtIndex(1, CalculationType.Sum);
|
||||
await tester.changeCalculateAtIndex(2, CalculationType.Min);
|
||||
|
||||
// Enter values in cells
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.Number,
|
||||
input: '100',
|
||||
);
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.Number,
|
||||
input: '150',
|
||||
);
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.Number,
|
||||
input: '50',
|
||||
cellIndex: 1,
|
||||
);
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.Number,
|
||||
input: '100',
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Dismiss edit cell
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.enter);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('250'), findsOneWidget);
|
||||
expect(find.text('50'), findsNWidgets(2));
|
||||
|
||||
// Delete 1st row
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
await tester.tapRowMenuButtonInGrid();
|
||||
await tester.tapDeleteOnRowMenu();
|
||||
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
expect(find.text('150'), findsNWidgets(2));
|
||||
expect(find.text('100'), findsNWidgets(2));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ void main() {
|
||||
File(path).writeAsStringSync(str);
|
||||
}
|
||||
// mock get files
|
||||
await mockPickFilePaths(
|
||||
mockPickFilePaths(
|
||||
paths: testFileNames
|
||||
.map((e) => p.join(context.applicationDataDirectory, e))
|
||||
.toList(),
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
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();
|
||||
const service = TestWorkspaceService(TestWorkspace.aiWorkSpace);
|
||||
|
||||
group('integration tests for open-ai smart menu', () {
|
||||
setUpAll(() async => await service.setUpAll());
|
||||
setUp(() async => await service.setUp());
|
||||
setUpAll(() async => service.setUpAll());
|
||||
setUp(() async => service.setUp());
|
||||
|
||||
testWidgets('testing selection on open-ai smart menu replace',
|
||||
(tester) async {
|
||||
@@ -58,7 +59,7 @@ void main() {
|
||||
|
||||
editorState.service.selectionService.updateSelection(
|
||||
Selection(
|
||||
start: Position(path: [1], offset: 0),
|
||||
start: Position(path: [1]),
|
||||
end: Position(path: [1], offset: 5),
|
||||
),
|
||||
);
|
||||
@@ -79,8 +80,8 @@ void main() {
|
||||
expect(
|
||||
editorState.service.selectionService.currentSelection.value,
|
||||
Selection(
|
||||
start: Position(path: [2], offset: 0),
|
||||
end: Position(path: [3], offset: 0),
|
||||
start: Position(path: [2]),
|
||||
end: Position(path: [3]),
|
||||
),
|
||||
);
|
||||
});
|
||||
@@ -98,7 +99,7 @@ Future<AppFlowyEditor> setUpOpenAITesting(WidgetTester tester) async {
|
||||
final Finder editor = find.byType(AppFlowyEditor);
|
||||
await tester.tap(editor);
|
||||
await tester.pumpAndSettle();
|
||||
return (tester.state(editor).widget as AppFlowyEditor);
|
||||
return tester.state(editor).widget as AppFlowyEditor;
|
||||
}
|
||||
|
||||
Future<void> mockOpenAIRepository() async {
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
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/notifications/widgets/notification_item.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/expectation.dart';
|
||||
import '../util/keyboard.dart';
|
||||
|
||||
void main() {
|
||||
@@ -35,7 +38,7 @@ void main() {
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger iline action menu and type 'remind tomorrow'
|
||||
// Trigger inline action menu and type 'remind tomorrow'
|
||||
final tomorrow = await _insertReminderTomorrow(tester);
|
||||
|
||||
Node node = tester.editor.getCurrentEditorState().getNodeAtPath([1])!;
|
||||
@@ -43,7 +46,7 @@ void main() {
|
||||
node.delta!.first.attributes![MentionBlockKeys.mention];
|
||||
|
||||
expect(node.type, 'paragraph');
|
||||
expect(mentionAttr['type'], MentionType.reminder.name);
|
||||
expect(mentionAttr['type'], MentionType.date.name);
|
||||
expect(mentionAttr['date'], tomorrow.toIso8601String());
|
||||
|
||||
await tester.tap(
|
||||
@@ -67,9 +70,57 @@ void main() {
|
||||
_dateWithTime(dateTimeSettings.timeFormat, tomorrow, time);
|
||||
|
||||
expect(node.type, 'paragraph');
|
||||
expect(mentionAttr['type'], MentionType.reminder.name);
|
||||
expect(mentionAttr['type'], MentionType.date.name);
|
||||
expect(mentionAttr['date'], tomorrowWithTime.toIso8601String());
|
||||
});
|
||||
|
||||
testWidgets('Add reminder for tomorrow, and navigate to it',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.getCurrentEditorState().insertNewLine();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger inline action menu and type 'remind tomorrow'
|
||||
final tomorrow = await _insertReminderTomorrow(tester);
|
||||
|
||||
final Node node =
|
||||
tester.editor.getCurrentEditorState().getNodeAtPath([1])!;
|
||||
final Map<String, dynamic> mentionAttr =
|
||||
node.delta!.first.attributes![MentionBlockKeys.mention];
|
||||
|
||||
expect(node.type, 'paragraph');
|
||||
expect(mentionAttr['type'], MentionType.date.name);
|
||||
expect(mentionAttr['date'], tomorrow.toIso8601String());
|
||||
|
||||
// Create and Navigate to a new document
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Open "Upcoming" in Notification hub
|
||||
await tester.openNotificationHub(tabIndex: 1);
|
||||
|
||||
// Expect 1 notification
|
||||
tester.expectNotificationItems(1);
|
||||
|
||||
// Tap on the notification
|
||||
await tester.tap(find.byType(NotificationItem));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect node at path 1 to be the date/reminder
|
||||
expect(
|
||||
tester.editor
|
||||
.getCurrentEditorState()
|
||||
.getNodeAtPath([1])
|
||||
?.delta
|
||||
?.first
|
||||
.attributes?[MentionBlockKeys.mention]['type'],
|
||||
MentionType.date.name,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
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_calendar_test.dart' as database_calendar_test;
|
||||
import 'database_cell_test.dart' as database_cell_test;
|
||||
import 'database_field_settings_test.dart' as database_field_settings_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 '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/user_icon_test.dart' as user_icon_test;
|
||||
import 'settings/user_language_test.dart' as user_language_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.
|
||||
///
|
||||
@@ -33,7 +32,7 @@ import 'tabs_test.dart' as tabs_test;
|
||||
/// 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.
|
||||
@@ -75,12 +74,7 @@ void main() {
|
||||
appearance_test_runner.main();
|
||||
|
||||
// User settings
|
||||
user_icon_test.main();
|
||||
user_language_test.main();
|
||||
|
||||
if (isCloudEnabled) {
|
||||
auth_test_runner.main();
|
||||
}
|
||||
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();
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import '../util/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Settings: user icon tests', () {
|
||||
testWidgets('select icon, select default option', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
|
||||
final userAvatarFinder = find.descendant(
|
||||
of: find.byType(SettingsUserView),
|
||||
matching: find.byType(UserAvatar),
|
||||
);
|
||||
|
||||
// Open icon picker dialog
|
||||
await tester.tap(userAvatarFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select first option that isn't default
|
||||
await tester.tap(find.byType(IconOption).first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
UserAvatar userAvatar = tester.widget(userAvatarFinder) as UserAvatar;
|
||||
expect(userAvatar.iconUrl, isNotEmpty);
|
||||
|
||||
// Open icon picker dialog again
|
||||
await tester.tap(userAvatarFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap the default option
|
||||
await tester.tap(
|
||||
find.descendant(
|
||||
of: find.byType(IconGallery),
|
||||
matching: find.byType(UserAvatar),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
userAvatar = tester.widget(userAvatarFinder) as UserAvatar;
|
||||
expect(userAvatar.iconUrl, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -14,7 +14,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapGoButton();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.language);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/rename_view_popover.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.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/keyboard.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Rename current view item', () {
|
||||
testWidgets('by F2 shortcut', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[LogicalKeyboardKey.f2],
|
||||
tester: tester,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(RenameViewPopover), findsOneWidget);
|
||||
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(RenameViewPopover),
|
||||
matching: find.byType(FlowyTextField),
|
||||
),
|
||||
'hello',
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Dismiss rename popover
|
||||
await tester.tap(find.byType(AppFlowyEditor));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(SingleInnerViewItem),
|
||||
matching: find.text('hello'),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/favorite_folder.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -31,17 +30,11 @@ void main() {
|
||||
].map((e) => 'document_$e').toList();
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
final parentName = i == 0 ? gettingStarted : names[i - 1];
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: names[i],
|
||||
parentName: parentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
tester.expectToSeePageName(
|
||||
names[i],
|
||||
parentName: parentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
parentLayout: ViewLayoutPB.Document,
|
||||
);
|
||||
tester.expectToSeePageName(names[i], parentName: parentName);
|
||||
}
|
||||
|
||||
await tester.favoriteViewByName(gettingStarted);
|
||||
@@ -87,7 +80,6 @@ void main() {
|
||||
await tester.favoriteViewByName(gettingStarted);
|
||||
await tester.hoverOnPageName(
|
||||
gettingStarted,
|
||||
layout: ViewLayoutPB.Document,
|
||||
onHover: () async {
|
||||
await tester.renamePage(name);
|
||||
await tester.pumpAndSettle();
|
||||
@@ -99,7 +91,7 @@ void main() {
|
||||
);
|
||||
expect(
|
||||
tester.findFavoritePageName(name),
|
||||
findsNothing,
|
||||
findsOneWidget,
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -113,10 +105,9 @@ void main() {
|
||||
final names = [1, 2].map((e) => 'document_$e').toList();
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
final parentName = i == 0 ? gettingStarted : names[i - 1];
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: names[i],
|
||||
parentName: parentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
tester.expectToSeePageName(names[i], parentName: parentName);
|
||||
}
|
||||
@@ -127,7 +118,7 @@ void main() {
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewItem &&
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite,
|
||||
),
|
||||
@@ -136,7 +127,6 @@ void main() {
|
||||
|
||||
await tester.hoverOnPageName(
|
||||
names[1],
|
||||
layout: ViewLayoutPB.Document,
|
||||
onHover: () async {
|
||||
await tester.tapDeletePageButton();
|
||||
await tester.pumpAndSettle();
|
||||
@@ -144,18 +134,12 @@ void main() {
|
||||
);
|
||||
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite,
|
||||
),
|
||||
tester.findAllFavoritePages(),
|
||||
findsNWidgets(3),
|
||||
);
|
||||
|
||||
await tester.hoverOnPageName(
|
||||
gettingStarted,
|
||||
layout: ViewLayoutPB.Document,
|
||||
onHover: () async {
|
||||
await tester.tapDeletePageButton();
|
||||
await tester.pumpAndSettle();
|
||||
@@ -163,12 +147,7 @@ void main() {
|
||||
);
|
||||
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite,
|
||||
),
|
||||
tester.findAllFavoritePages(),
|
||||
findsNothing,
|
||||
);
|
||||
},
|
||||
@@ -180,7 +159,7 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
await tester.favoriteViewByName(gettingStarted);
|
||||
expect(
|
||||
find.byWidgetPredicate(
|
||||
@@ -200,11 +179,10 @@ void main() {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
await tester.createNewPageWithName();
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
await tester.favoriteViewByName(gettingStarted);
|
||||
await tester.hoverOnPageName(
|
||||
gettingStarted,
|
||||
layout: ViewLayoutPB.Document,
|
||||
useLast: false,
|
||||
onHover: () async {
|
||||
await tester.tapPageOptionButton();
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
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/base.dart';
|
||||
import '../util/common_operations.dart';
|
||||
import '../util/expectation.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
const emoji = '😁';
|
||||
|
||||
group('Icon', () {
|
||||
testWidgets('Update page icon in sidebar', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create document, board, grid and calendar views
|
||||
for (final value in ViewLayoutPB.values) {
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
layout: value,
|
||||
);
|
||||
|
||||
// update its icon
|
||||
await tester.updatePageIconInSidebarByName(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
layout: value,
|
||||
icon: emoji,
|
||||
);
|
||||
|
||||
tester.expectViewHasIcon(
|
||||
value.name,
|
||||
value,
|
||||
emoji,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('Update page icon in title bar', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapGoButton();
|
||||
|
||||
// create document, board, grid and calendar views
|
||||
for (final value in ViewLayoutPB.values) {
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
layout: value,
|
||||
);
|
||||
|
||||
// update its icon
|
||||
await tester.updatePageIconInTitleBarByName(
|
||||
name: value.name,
|
||||
layout: value,
|
||||
icon: emoji,
|
||||
);
|
||||
|
||||
tester.expectViewHasIcon(
|
||||
value.name,
|
||||
value,
|
||||
emoji,
|
||||
);
|
||||
|
||||
tester.expectViewTitleHasIcon(
|
||||
value.name,
|
||||
value,
|
||||
emoji,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
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/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/workspace/presentation/home/menu/view/draggable_view_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_add_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_button.dart';
|
||||
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:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -41,7 +41,7 @@ void main() {
|
||||
for (final layout in ViewLayoutPB.values) {
|
||||
// create a new page
|
||||
final name = 'AppFlowy_$layout';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: layout,
|
||||
);
|
||||
@@ -79,10 +79,9 @@ void main() {
|
||||
final names = [1, 2, 3, 4].map((e) => 'document_$e').toList();
|
||||
for (var i = 0; i < names.length; i++) {
|
||||
final parentName = i == 0 ? gettingStarted : names[i - 1];
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: names[i],
|
||||
parentName: parentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
tester.expectToSeePageName(names[i], parentName: parentName);
|
||||
}
|
||||
@@ -144,14 +143,14 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
const document = 'document';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: document,
|
||||
openAfterCreated: false,
|
||||
);
|
||||
tester.expectToSeePageName(document, layout: ViewLayoutPB.Document);
|
||||
tester.expectToSeePageName(document);
|
||||
|
||||
const grid = 'grid';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: grid,
|
||||
layout: ViewLayoutPB.Grid,
|
||||
openAfterCreated: false,
|
||||
@@ -187,10 +186,9 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
const grid = 'grid';
|
||||
await tester.createNewPageWithName(
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: grid,
|
||||
layout: ViewLayoutPB.Grid,
|
||||
openAfterCreated: true,
|
||||
);
|
||||
tester.expectToSeePageName(grid, layout: ViewLayoutPB.Grid);
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'sidebar_test.dart' as sidebar_test;
|
||||
import 'sidebar_expand_test.dart' as sidebar_expanded_test;
|
||||
import 'sidebar_favorites_test.dart' as sidebar_favorite_test;
|
||||
import 'sidebar_icon_test.dart' as sidebar_icon_test;
|
||||
import 'sidebar_test.dart' as sidebar_test;
|
||||
|
||||
void startTesting() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -11,4 +12,5 @@ void startTesting() {
|
||||
sidebar_test.main();
|
||||
sidebar_expanded_test.main();
|
||||
sidebar_favorite_test.main();
|
||||
sidebar_icon_test.main();
|
||||
}
|
||||
@@ -33,7 +33,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.tapGoButton();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// switch to user B
|
||||
{
|
||||
@@ -51,7 +51,7 @@ void main() {
|
||||
);
|
||||
await tester.tapCustomLocationButton();
|
||||
await tester.pumpAndSettle();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// set user name for userB
|
||||
await tester.openSettings();
|
||||
@@ -71,7 +71,7 @@ void main() {
|
||||
await tester.tapCustomLocationButton();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
tester.expectToSeeUserName(userA);
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ void main() {
|
||||
await tester.tapCustomLocationButton();
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
tester.expectToSeeUserName(userB);
|
||||
}
|
||||
});
|
||||
@@ -99,7 +99,7 @@ void main() {
|
||||
await tester.tapGoButton();
|
||||
|
||||
// home and readme document
|
||||
tester.expectToSeeHomePage();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// open settings and restore the location
|
||||
await tester.openSettings();
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:appflowy/workspace/presentation/home/tabs/flowy_tab.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/tabs/tabs_manager.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.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';
|
||||
@@ -32,15 +32,9 @@ void main() {
|
||||
findsNothing,
|
||||
);
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
name: _documentName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent(name: _documentName);
|
||||
|
||||
await tester.createNewPageWithName(
|
||||
name: _documentTwoName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.createNewPageWithNameUnderParent(name: _documentTwoName);
|
||||
|
||||
/// Open second menu item in a new tab
|
||||
await tester.openAppInNewTab(gettingStarted, ViewLayoutPB.Document);
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/sync_setting_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.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/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'base.dart';
|
||||
import 'expectation.dart';
|
||||
|
||||
extension AppFlowyAuthTest on WidgetTester {
|
||||
Future<void> tapGoogleLoginInButton() async {
|
||||
await tapButton(find.byKey(const Key('signInWithGoogleButton')));
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
await tapButton(find.byType(SettingLogoutButton));
|
||||
|
||||
expectToSeeText(LocaleKeys.button_ok.tr());
|
||||
await tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
}
|
||||
|
||||
Future<void> tapSignInAsGuest() async {
|
||||
await tapButton(find.byType(SignInAnonymousButton));
|
||||
}
|
||||
@@ -34,10 +46,20 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
);
|
||||
}
|
||||
|
||||
void assertEnableSyncSwitchValue(bool value) {
|
||||
void assertSupabaseEnableSyncSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
find.descendant(
|
||||
of: find.byType(EnableSync),
|
||||
of: find.byType(SupabaseEnableSync),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
),
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
void assertAppFlowyCloudEnableSyncSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyCloudEnableSync),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
),
|
||||
value,
|
||||
@@ -53,9 +75,9 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
await tapButton(finder);
|
||||
}
|
||||
|
||||
Future<void> toggleEnableSync() async {
|
||||
Future<void> toggleEnableSync(Type syncButton) async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(EnableSync),
|
||||
of: find.byType(syncButton),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/env/cloud_env_test.dart';
|
||||
import 'package:appflowy/startup/entry_point.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/user/application/auth/supabase_mock_auth_service.dart';
|
||||
import 'package:appflowy/user/presentation/presentation.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@@ -26,25 +31,76 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
Future<FlowyTestContext> initializeAppFlowy({
|
||||
// use to append after the application data directory
|
||||
String? pathExtension,
|
||||
Size windowsSize = const Size(1600, 1200),
|
||||
// use to specify the application data directory, if not specified, a temporary directory will be used.
|
||||
String? dataDirectory,
|
||||
Size windowSize = const Size(1600, 1200),
|
||||
AuthenticatorType? cloudType,
|
||||
String? email,
|
||||
}) async {
|
||||
binding.setSurfaceSize(windowsSize);
|
||||
// view.physicalSize = windowsSize;
|
||||
await binding.setSurfaceSize(windowSize);
|
||||
// addTearDown(() => binding.setSurfaceSize(null));
|
||||
|
||||
mockHotKeyManagerHandlers();
|
||||
final directory = await mockApplicationDataStorage(
|
||||
pathExtension: pathExtension,
|
||||
);
|
||||
final applicationDataDirectory = dataDirectory ??
|
||||
await mockApplicationDataStorage(
|
||||
pathExtension: pathExtension,
|
||||
);
|
||||
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await FlowyRunner.run(
|
||||
FlowyApp(),
|
||||
AppFlowyApplication(),
|
||||
IntegrationMode.integrationTest,
|
||||
rustEnvsBuilder: () {
|
||||
final rustEnvs = <String, String>{};
|
||||
if (cloudType != null) {
|
||||
switch (cloudType) {
|
||||
case AuthenticatorType.local:
|
||||
break;
|
||||
case AuthenticatorType.supabase:
|
||||
break;
|
||||
case AuthenticatorType.appflowyCloudSelfHost:
|
||||
rustEnvs["GOTRUE_ADMIN_EMAIL"] = "admin@example.com";
|
||||
rustEnvs["GOTRUE_ADMIN_PASSWORD"] = "password";
|
||||
break;
|
||||
default:
|
||||
throw Exception("not supported");
|
||||
}
|
||||
}
|
||||
return rustEnvs;
|
||||
},
|
||||
didInitGetItCallback: () {
|
||||
return Future(
|
||||
() async {
|
||||
if (cloudType != null) {
|
||||
switch (cloudType) {
|
||||
case AuthenticatorType.local:
|
||||
await useLocal();
|
||||
break;
|
||||
case AuthenticatorType.supabase:
|
||||
await useTestSupabaseCloud();
|
||||
getIt.unregister<AuthService>();
|
||||
getIt.registerFactory<AuthService>(
|
||||
() => SupabaseMockAuthService(),
|
||||
);
|
||||
break;
|
||||
case AuthenticatorType.appflowyCloudSelfHost:
|
||||
await useTestSelfHostedAppFlowyCloud();
|
||||
getIt.unregister<AuthService>();
|
||||
getIt.registerFactory<AuthService>(
|
||||
() => AppFlowyCloudMockAuthService(email: email),
|
||||
);
|
||||
default:
|
||||
throw Exception("not supported");
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
await waitUntilSignInPageShow();
|
||||
|
||||
return FlowyTestContext(
|
||||
applicationDataDirectory: directory,
|
||||
applicationDataDirectory: applicationDataDirectory,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,43 +115,34 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> mockApplicationDataStorage({
|
||||
// use to append after the application data directory
|
||||
String? pathExtension,
|
||||
}) async {
|
||||
final dir = await getTemporaryDirectory();
|
||||
|
||||
// Use a random uuid to avoid conflict.
|
||||
String path = p.join(dir.path, 'appflowy_integration_test', uuid());
|
||||
if (pathExtension != null && pathExtension.isNotEmpty) {
|
||||
path = '$path/$pathExtension';
|
||||
Future<void> waitUntilSignInPageShow() async {
|
||||
if (isAuthEnabled) {
|
||||
final finder = find.byType(SignInAnonymousButton);
|
||||
await pumpUntilFound(finder);
|
||||
expect(finder, findsOneWidget);
|
||||
} else {
|
||||
final finder = find.byType(GoButton);
|
||||
await pumpUntilFound(finder);
|
||||
expect(finder, findsOneWidget);
|
||||
}
|
||||
final directory = Directory(path);
|
||||
if (!directory.existsSync()) {
|
||||
await directory.create(recursive: true);
|
||||
}
|
||||
|
||||
MockApplicationDataStorage.initialPath = directory.path;
|
||||
|
||||
return directory.path;
|
||||
}
|
||||
|
||||
Future<void> waitUntilSignInPageShow() async {
|
||||
final finder = find.byType(GoButton);
|
||||
await pumpUntilFound(finder);
|
||||
expect(finder, findsOneWidget);
|
||||
Future<void> waitForSeconds(int seconds) async {
|
||||
await Future.delayed(Duration(seconds: seconds), () {});
|
||||
}
|
||||
|
||||
Future<void> pumpUntilFound(
|
||||
Finder finder, {
|
||||
Duration timeout = const Duration(seconds: 10),
|
||||
Duration pumpInterval =
|
||||
const Duration(milliseconds: 50), // Interval between pumps
|
||||
}) async {
|
||||
bool timerDone = false;
|
||||
final timer = Timer(timeout, () => timerDone = true);
|
||||
while (timerDone != true) {
|
||||
await pump();
|
||||
while (!timerDone) {
|
||||
await pump(pumpInterval); // Pump with an interval
|
||||
if (any(finder)) {
|
||||
timerDone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
timer.cancel();
|
||||
@@ -199,3 +246,39 @@ extension AppFlowyFinderTestBase on CommonFinders {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> useLocal() async {
|
||||
await useLocalServer();
|
||||
}
|
||||
|
||||
Future<void> useTestSupabaseCloud() async {
|
||||
await useSupabaseCloud(
|
||||
url: TestEnv.supabaseUrl,
|
||||
anonKey: TestEnv.supabaseAnonKey,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> useTestSelfHostedAppFlowyCloud() async {
|
||||
await useSelfHostedAppFlowyCloudWithURL(TestEnv.afCloudUrl);
|
||||
}
|
||||
|
||||
Future<String> mockApplicationDataStorage({
|
||||
// use to append after the application data directory
|
||||
String? pathExtension,
|
||||
}) async {
|
||||
final dir = await getTemporaryDirectory();
|
||||
|
||||
// Use a random uuid to avoid conflict.
|
||||
String path = p.join(dir.path, 'appflowy_integration_test', uuid());
|
||||
if (pathExtension != null && pathExtension.isNotEmpty) {
|
||||
path = '$path/$pathExtension';
|
||||
}
|
||||
final directory = Directory(path);
|
||||
if (!directory.existsSync()) {
|
||||
await directory.create(recursive: true);
|
||||
}
|
||||
|
||||
MockApplicationDataStorage.initialPath = directory.path;
|
||||
|
||||
return directory.path;
|
||||
}
|
||||
@@ -4,17 +4,23 @@ import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/base/emoji_picker_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/presentation/screens/screens.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_new_page_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_add_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/flowy_tab.dart';
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_tab_bar.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
@@ -22,13 +28,21 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'emoji.dart';
|
||||
import 'util.dart';
|
||||
|
||||
extension CommonOperations on WidgetTester {
|
||||
/// Tap the GetStart button on the launch page.
|
||||
Future<void> tapGoButton() async {
|
||||
// local version
|
||||
final goButton = find.byType(GoButton);
|
||||
await tapButton(goButton);
|
||||
if (goButton.evaluate().isNotEmpty) {
|
||||
await tapButton(goButton);
|
||||
} else {
|
||||
// cloud version
|
||||
final anonymousButton = find.byType(SignInAnonymousButton);
|
||||
await tapButton(anonymousButton);
|
||||
}
|
||||
|
||||
if (Platform.isWindows) {
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
@@ -279,7 +293,7 @@ extension CommonOperations on WidgetTester {
|
||||
await tapButton(markdownButton);
|
||||
}
|
||||
|
||||
Future<void> createNewPageWithName({
|
||||
Future<void> createNewPageWithNameUnderParent({
|
||||
String? name,
|
||||
ViewLayoutPB layout = ViewLayoutPB.Document,
|
||||
String? parentName,
|
||||
@@ -292,7 +306,7 @@ extension CommonOperations on WidgetTester {
|
||||
KVKeys.showRenameDialogWhenCreatingNewFile,
|
||||
(value) => bool.parse(value),
|
||||
);
|
||||
final showRenameDialog = settingsOrFailure.fold((l) => false, (r) => r);
|
||||
final showRenameDialog = settingsOrFailure.fold(() => false, (r) => r);
|
||||
if (showRenameDialog) {
|
||||
await tapOKButton();
|
||||
}
|
||||
@@ -322,6 +336,13 @@ extension CommonOperations on WidgetTester {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createNewPage({
|
||||
ViewLayoutPB layout = ViewLayoutPB.Document,
|
||||
bool openAfterCreated = true,
|
||||
}) async {
|
||||
await tapButton(find.byType(SidebarNewPageButton));
|
||||
}
|
||||
|
||||
Future<void> simulateKeyEvent(
|
||||
LogicalKeyboardKey key, {
|
||||
bool isControlPressed = false,
|
||||
@@ -376,7 +397,6 @@ extension CommonOperations on WidgetTester {
|
||||
await hoverOnPageName(
|
||||
name,
|
||||
layout: layout,
|
||||
useLast: true,
|
||||
onHover: () async {
|
||||
await tapFavoritePageButton();
|
||||
await pumpAndSettle();
|
||||
@@ -391,7 +411,6 @@ extension CommonOperations on WidgetTester {
|
||||
await hoverOnPageName(
|
||||
name,
|
||||
layout: layout,
|
||||
useLast: true,
|
||||
onHover: () async {
|
||||
await tapUnfavoritePageButton();
|
||||
await pumpAndSettle();
|
||||
@@ -434,6 +453,71 @@ extension CommonOperations on WidgetTester {
|
||||
);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
// update the page icon in the sidebar
|
||||
Future<void> updatePageIconInSidebarByName({
|
||||
required String name,
|
||||
required String parentName,
|
||||
required ViewLayoutPB layout,
|
||||
required String icon,
|
||||
}) async {
|
||||
final iconButton = find.descendant(
|
||||
of: findPageName(
|
||||
name,
|
||||
layout: layout,
|
||||
parentName: parentName,
|
||||
),
|
||||
matching:
|
||||
find.byTooltip(LocaleKeys.document_plugins_cover_changeIcon.tr()),
|
||||
);
|
||||
await tapButton(iconButton);
|
||||
await tapEmoji(icon);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
// update the page icon in the sidebar
|
||||
Future<void> updatePageIconInTitleBarByName({
|
||||
required String name,
|
||||
required ViewLayoutPB layout,
|
||||
required String icon,
|
||||
}) async {
|
||||
await openPage(
|
||||
name,
|
||||
layout: layout,
|
||||
);
|
||||
final title = find.descendant(
|
||||
of: find.byType(ViewTitleBar),
|
||||
matching: find.text(name),
|
||||
);
|
||||
await tapButton(title);
|
||||
await tapButton(find.byType(EmojiPickerButton));
|
||||
await tapEmoji(icon);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> openNotificationHub({
|
||||
int tabIndex = 0,
|
||||
}) async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(NotificationButton),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svg == FlowySvgs.clock_alarm_s,
|
||||
),
|
||||
);
|
||||
|
||||
await tap(finder);
|
||||
await pumpAndSettle();
|
||||
|
||||
if (tabIndex == 1) {
|
||||
final tabFinder = find.descendant(
|
||||
of: find.byType(NotificationTabBar),
|
||||
matching: find.byType(FlowyTabItem).at(1),
|
||||
);
|
||||
|
||||
await tap(tabFinder);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ViewLayoutPBTest on ViewLayoutPB {
|
||||
|
||||
@@ -1,77 +1,96 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/calculations/calculation_type_ext.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculate_cell.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/number.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/checklist.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/date.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/number.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/select_option.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/timestamp.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/url.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
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/application/calendar_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_day.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_event_card.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_event_editor.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/presentation/calendar_page.dart';
|
||||
import 'package:appflowy/plugins/database_view/calendar/presentation/toolbar/calendar_layout_setting.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/choicechip/text.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/create_filter_list.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/disclosure_button.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_menu_item.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/footer/grid_footer.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_cell_action_sheet.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_editor.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_extension.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_list.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/field_type_option_editor.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/date.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/timestamp.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/row/row.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/create_sort_list.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/order_panel.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_editor.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_menu.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/filter_button.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/grid_layout.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/toolbar/sort_button.dart';
|
||||
import 'package:appflowy/plugins/database_view/tar_bar/tab_bar_header.dart';
|
||||
import 'package:appflowy/plugins/database_view/tar_bar/tar_bar_add_button.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/database_layout_ext.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/setting/setting_property_list.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/accessory/cell_accessory.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/cells.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_cell_editor.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/checklist_cell/checklist_progress_bar.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/date_cell/date_editor.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/select_option_editor.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/text_field.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_action.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_banner.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_document.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/row/row_property.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/setting/setting_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_menu_item.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/board_page.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/application/calendar_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/calendar_day.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/calendar_event_card.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/calendar_event_editor.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/calendar_page.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/toolbar/calendar_layout_setting.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/text.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/create_filter_list.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/disclosure_button.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_menu_item.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/footer/grid_footer.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/desktop_field_cell.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_editor.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_list.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/type_option/date/date_time_format.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/row/row.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/create_sort_list.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/order_panel.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_editor.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_menu.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/filter_button.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/toolbar/sort_button.dart';
|
||||
import 'package:appflowy/plugins/database/tab_bar/desktop/tab_bar_add_button.dart';
|
||||
import 'package:appflowy/plugins/database/tab_bar/desktop/tab_bar_header.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/database_layout_ext.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/checklist_cell_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/checklist_progress_bar.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/date_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_text_field.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_action.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_banner.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_document.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_property.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/database_layout_selector.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/database_setting_action.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/database_settings_list.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/setting_button.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/setting/setting_property_list.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/clear_date_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/date_type_option_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/end_time_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
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:appflowy_board/appflowy_board.dart';
|
||||
import 'package:calendar_view/calendar_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_input.dart';
|
||||
import 'package:flowy_infra_ui/widget/buttons/primary_button.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
// Non-exported member of the table_calendar library
|
||||
import 'package:table_calendar/src/widgets/cell_content.dart';
|
||||
|
||||
import 'base.dart';
|
||||
import 'common_operations.dart';
|
||||
import 'expectation.dart';
|
||||
@@ -105,7 +124,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
File(path).writeAsStringSync(str);
|
||||
}
|
||||
// mock get files
|
||||
await mockPickFilePaths(
|
||||
mockPickFilePaths(
|
||||
paths: paths,
|
||||
);
|
||||
await tapDatabaseRawDataButton();
|
||||
@@ -134,7 +153,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
///
|
||||
Finder cellFinder(int rowIndex, FieldType fieldType, {int cellIndex = 0}) {
|
||||
final findRow = find.byType(GridRow, skipOffstage: false);
|
||||
final findCell = finderForFieldType(fieldType);
|
||||
@@ -166,10 +184,14 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
required bool isSelected,
|
||||
}) async {
|
||||
final cell = cellFinder(rowIndex, FieldType.Checkbox);
|
||||
var finder = find.byType(CheckboxCellUncheck);
|
||||
if (isSelected) {
|
||||
finder = find.byType(CheckboxCellCheck);
|
||||
}
|
||||
final finder = isSelected
|
||||
? find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowySvg && widget.svg == FlowySvgs.check_filled_s,
|
||||
)
|
||||
: find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svg == FlowySvgs.uncheck_s,
|
||||
);
|
||||
|
||||
expect(
|
||||
find.descendant(
|
||||
@@ -186,30 +208,23 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}) async {
|
||||
final cell = cellFinder(rowIndex, fieldType);
|
||||
expect(cell, findsOneWidget);
|
||||
await tapButton(cell, warnIfMissed: false);
|
||||
await tapButton(cell);
|
||||
}
|
||||
|
||||
/// The [fieldName] must be unique in the grid.
|
||||
Future<void> assertCellContent({
|
||||
void assertCellContent({
|
||||
required int rowIndex,
|
||||
required FieldType fieldType,
|
||||
required String content,
|
||||
int cellIndex = 0,
|
||||
}) async {
|
||||
}) {
|
||||
final findCell = cellFinder(rowIndex, fieldType, cellIndex: cellIndex);
|
||||
final findContent = find.descendant(
|
||||
of: findCell,
|
||||
matching: find.text(content),
|
||||
skipOffstage: false,
|
||||
);
|
||||
|
||||
final text = find.descendant(
|
||||
of: find.byType(TextField),
|
||||
matching: findContent,
|
||||
skipOffstage: false,
|
||||
);
|
||||
|
||||
expect(text, findsOneWidget);
|
||||
expect(findContent, findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> assertSingleSelectOption({
|
||||
@@ -221,7 +236,9 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final finder = find.descendant(
|
||||
of: findCell,
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) => widget is SelectOptionTag && widget.name == content,
|
||||
(widget) =>
|
||||
widget is SelectOptionTag &&
|
||||
(widget.name == content || widget.option?.name == content),
|
||||
),
|
||||
);
|
||||
expect(finder, findsOneWidget);
|
||||
@@ -238,7 +255,9 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final finder = find.descendant(
|
||||
of: findCell,
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) => widget is SelectOptionTag && widget.name == content,
|
||||
(widget) =>
|
||||
widget is SelectOptionTag &&
|
||||
(widget.name == content || widget.option?.name == content),
|
||||
),
|
||||
);
|
||||
expect(finder, findsOneWidget);
|
||||
@@ -247,10 +266,10 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
/// null percent means no progress bar should be found
|
||||
Future<void> assertChecklistCellInGrid({
|
||||
void assertChecklistCellInGrid({
|
||||
required int rowIndex,
|
||||
required double? percent,
|
||||
}) async {
|
||||
}) {
|
||||
final findCell = cellFinder(rowIndex, FieldType.Checklist);
|
||||
|
||||
if (percent == null) {
|
||||
@@ -263,49 +282,14 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final finder = find.descendant(
|
||||
of: findCell,
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) {
|
||||
if (widget is ChecklistProgressBar) {
|
||||
return widget.percent == percent;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(widget) =>
|
||||
widget is ChecklistProgressBar && widget.percent == percent,
|
||||
),
|
||||
);
|
||||
expect(finder, findsOneWidget);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> assertDateCellInGrid({
|
||||
required int rowIndex,
|
||||
required String content,
|
||||
}) async {
|
||||
final findRow = find.byType(GridRow, skipOffstage: false);
|
||||
final findCell = find.descendant(
|
||||
of: findRow.at(rowIndex),
|
||||
matching: find.byType(GridDateCell),
|
||||
skipOffstage: false,
|
||||
);
|
||||
|
||||
final dateCellText = find.descendant(
|
||||
of: findCell,
|
||||
matching: find.byType(GridDateCellText),
|
||||
);
|
||||
|
||||
final text = find.descendant(
|
||||
of: dateCellText,
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) {
|
||||
if (widget is FlowyText) {
|
||||
return widget.text == content;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
),
|
||||
skipOffstage: false,
|
||||
);
|
||||
expect(text, findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> selectDay({
|
||||
required int content,
|
||||
}) async {
|
||||
@@ -340,6 +324,26 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(finder);
|
||||
}
|
||||
|
||||
Future<void> selectReminderOption(ReminderOption option) async {
|
||||
await tapButton(find.byType(ReminderSelector));
|
||||
|
||||
final finder = find.descendant(
|
||||
of: find.byType(FlowyButton),
|
||||
matching: find.textContaining(option.label),
|
||||
);
|
||||
|
||||
await tapButton(finder);
|
||||
}
|
||||
|
||||
Future<bool> selectLastDateInPicker() async {
|
||||
final finder = find.byType(CellContent).last;
|
||||
final w = widget(finder) as CellContent;
|
||||
|
||||
await tapButton(finder);
|
||||
|
||||
return w.isToday;
|
||||
}
|
||||
|
||||
Future<void> toggleDateRange() async {
|
||||
final findDateEditor = find.byType(EndTimeButton);
|
||||
final findToggle = find.byType(Toggle);
|
||||
@@ -350,15 +354,11 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(finder);
|
||||
}
|
||||
|
||||
Future<void> changeDateFormat() async {
|
||||
final findDateEditor = find.byType(DateCellEditor);
|
||||
final findDateTimeOptionButton = find.byType(DateTypeOptionButton);
|
||||
final finder = find.descendant(
|
||||
of: findDateEditor,
|
||||
matching: findDateTimeOptionButton,
|
||||
);
|
||||
await tapButton(finder);
|
||||
Future<void> tapChangeDateTimeFormatButton() async {
|
||||
await tapButton(find.byType(DateTypeOptionButton));
|
||||
}
|
||||
|
||||
Future<void> changeDateFormat() async {
|
||||
final findDateFormatButton = find.byType(DateFormatButton);
|
||||
await tapButton(findDateFormatButton);
|
||||
|
||||
@@ -367,14 +367,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> changeTimeFormat() async {
|
||||
final findDateEditor = find.byType(DateCellEditor);
|
||||
final findDateTimeOptionButton = find.byType(DateTypeOptionButton);
|
||||
final finder = find.descendant(
|
||||
of: findDateEditor,
|
||||
matching: findDateTimeOptionButton,
|
||||
);
|
||||
await tapButton(finder);
|
||||
|
||||
final findDateFormatButton = find.byType(TimeFormatButton);
|
||||
await tapButton(findDateFormatButton);
|
||||
|
||||
@@ -444,7 +436,9 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}) async {
|
||||
final findRow = find.byType(GridRow);
|
||||
final option = find.byWidgetPredicate(
|
||||
(widget) => widget is SelectOptionTag && widget.name == name,
|
||||
(widget) =>
|
||||
widget is SelectOptionTag &&
|
||||
(widget.name == name || widget.option?.name == name),
|
||||
);
|
||||
|
||||
final cell = find.descendant(
|
||||
@@ -486,7 +480,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
void assertChecklistEditorVisible({required bool visible}) {
|
||||
final editor = find.byType(GridChecklistCellEditor);
|
||||
final editor = find.byType(ChecklistCellEditor);
|
||||
if (visible) {
|
||||
expect(editor, findsOneWidget);
|
||||
} else {
|
||||
@@ -599,7 +593,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> editTitleInRowDetailPage(String title) async {
|
||||
final titleField = find.byType(GridTextCell);
|
||||
final titleField = find.byType(EditableTextCell);
|
||||
await enterText(titleField, title);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
@@ -617,12 +611,11 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> openEmojiPicker() async {
|
||||
await tapButton(find.byType(EmojiPickerButton));
|
||||
await tapButton(find.byType(EmojiSelectionMenu));
|
||||
await tapButton(find.byType(AddEmojiButton));
|
||||
}
|
||||
|
||||
Future<void> tapDateCellInRowDetailPage() async {
|
||||
final findDateCell = find.byType(GridDateCell);
|
||||
final findDateCell = find.byType(EditableDateCell);
|
||||
await tapButton(findDateCell);
|
||||
}
|
||||
|
||||
@@ -688,7 +681,10 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> tapDeletePropertyInFieldEditor() async {
|
||||
final deleteButton = find.byType(DeleteFieldButton);
|
||||
final deleteButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell && widget.action == FieldAction.delete,
|
||||
);
|
||||
await tapButton(deleteButton);
|
||||
|
||||
final confirmButton = find.descendant(
|
||||
@@ -710,7 +706,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
Future<void> scrollToRight(Finder find) async {
|
||||
final size = getSize(find);
|
||||
await drag(find, Offset(-size.width, 0));
|
||||
await drag(find, Offset(-size.width, 0), warnIfMissed: false);
|
||||
await pumpAndSettle(const Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
@@ -727,8 +723,33 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> changeFieldTypeOfFieldWithName(
|
||||
String name,
|
||||
FieldType type,
|
||||
) async {
|
||||
await tapGridFieldWithName(name);
|
||||
await tapEditFieldButton();
|
||||
|
||||
await tapSwitchFieldTypeButton();
|
||||
await selectFieldType(type);
|
||||
await dismissFieldEditor();
|
||||
}
|
||||
|
||||
Future<void> changeCalculateAtIndex(int index, CalculationType type) async {
|
||||
await tap(find.byType(CalculateCell).at(index));
|
||||
await pumpAndSettle();
|
||||
|
||||
await tap(
|
||||
find.descendant(
|
||||
of: find.byType(CalculationTypeItem),
|
||||
matching: find.text(type.label),
|
||||
),
|
||||
);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
/// Should call [tapGridFieldWithName] first.
|
||||
Future<void> tapEditPropertyButton() async {
|
||||
Future<void> tapEditFieldButton() async {
|
||||
await tapButtonWithName(LocaleKeys.grid_field_editProperty.tr());
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
@@ -742,7 +763,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(field);
|
||||
}
|
||||
|
||||
/// Should call [tapGridFieldWithName] first.
|
||||
/// A SimpleDialog must be shown first, e.g. when deleting a field.
|
||||
Future<void> tapDialogOkButton() async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
@@ -761,17 +782,36 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(field);
|
||||
}
|
||||
|
||||
Future<void> tapInsertFieldButton({
|
||||
required bool left,
|
||||
required String name,
|
||||
}) async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell &&
|
||||
(left && widget.action == FieldAction.insertLeft ||
|
||||
!left && widget.action == FieldAction.insertRight),
|
||||
);
|
||||
await tapButton(field);
|
||||
await renameField(name);
|
||||
}
|
||||
|
||||
/// Should call [tapGridFieldWithName] first.
|
||||
Future<void> tapHidePropertyButton() async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell && widget.action == FieldAction.hide,
|
||||
widget is FieldActionCell &&
|
||||
widget.action == FieldAction.toggleVisibility,
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
|
||||
Future<void> tapHidePropertyButtonInFieldEditor() async {
|
||||
final button = find.byType(FieldVisibilityToggleButton);
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell &&
|
||||
widget.action == FieldAction.toggleVisibility,
|
||||
);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
@@ -791,7 +831,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(find.byType(RowDetailPageDuplicateButton));
|
||||
}
|
||||
|
||||
Future<void> tapTypeOptionButton() async {
|
||||
Future<void> tapSwitchFieldTypeButton() async {
|
||||
await tapButton(find.byType(SwitchFieldButton));
|
||||
}
|
||||
|
||||
@@ -799,18 +839,29 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
}
|
||||
|
||||
/// Must call [tapTypeOptionButton] first.
|
||||
/// Must call [tapSwitchFieldTypeButton] first.
|
||||
Future<void> selectFieldType(FieldType fieldType) async {
|
||||
final fieldTypeCell = find.byType(FieldTypeCell);
|
||||
final fieldTypeButton = find.descendant(
|
||||
of: fieldTypeCell,
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == fieldType.title(),
|
||||
(widget) => widget is FlowyText && widget.text == fieldType.i18n,
|
||||
),
|
||||
);
|
||||
await tapButton(fieldTypeButton);
|
||||
}
|
||||
|
||||
// Use in edit mode of FieldEditor
|
||||
void expectEmptyTypeOptionEditor() {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(FieldTypeOptionEditor),
|
||||
matching: find.byType(TypeOptionSeparator),
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
}
|
||||
|
||||
/// Each field has its own cell, so we can find the corresponding cell by
|
||||
/// the field type after create a new field.
|
||||
Future<void> findCellByFieldType(FieldType fieldType) async {
|
||||
@@ -860,14 +911,14 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
expect(widget.field.fieldType, fieldType);
|
||||
}
|
||||
|
||||
Future<void> findFieldWithName(String name) async {
|
||||
void findFieldWithName(String name) {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) => widget is FieldCellButton && widget.field.name == name,
|
||||
);
|
||||
expect(field, findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> noFieldWithName(String name) async {
|
||||
void noFieldWithName(String name) {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) => widget is FieldCellButton && widget.field.name == name,
|
||||
);
|
||||
@@ -932,7 +983,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await scrollToRight(find.byType(GridPage));
|
||||
await tapNewPropertyButton();
|
||||
await renameField(name);
|
||||
await tapTypeOptionButton();
|
||||
await tapSwitchFieldTypeButton();
|
||||
await selectFieldType(fieldType);
|
||||
await dismissFieldEditor();
|
||||
}
|
||||
@@ -1019,6 +1070,34 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(findSortItem);
|
||||
}
|
||||
|
||||
/// Must call [tapSortMenuInSettingBar] first.
|
||||
Future<void> reorderSort(
|
||||
(FieldType, String) from,
|
||||
(FieldType, String) to,
|
||||
) async {
|
||||
final fromSortItem = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is DatabaseSortItem &&
|
||||
widget.sortInfo.fieldInfo.fieldType == from.$1 &&
|
||||
widget.sortInfo.fieldInfo.name == from.$2,
|
||||
);
|
||||
final toSortItem = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is DatabaseSortItem &&
|
||||
widget.sortInfo.fieldInfo.fieldType == to.$1 &&
|
||||
widget.sortInfo.fieldInfo.name == to.$2,
|
||||
);
|
||||
final dragElement = find.descendant(
|
||||
of: fromSortItem,
|
||||
matching: find.byType(ReorderableDragStartListener),
|
||||
);
|
||||
await drag(
|
||||
dragElement,
|
||||
getCenter(toSortItem) - getCenter(fromSortItem),
|
||||
);
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
|
||||
/// Must call [tapSortButtonByName] first.
|
||||
Future<void> tapSortByDescending() async {
|
||||
await tapButton(
|
||||
@@ -1090,7 +1169,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
skipOffstage: false,
|
||||
);
|
||||
expect(findCell, findsOneWidget);
|
||||
await tapButton(findCell, warnIfMissed: false);
|
||||
await tapButton(findCell);
|
||||
}
|
||||
|
||||
Future<void> tapCheckedButtonOnCheckboxFilter() async {
|
||||
@@ -1131,7 +1210,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
/// Should call [tapDatabaseSettingButton] first.
|
||||
Future<void> tapViewPropertiesButton() async {
|
||||
final findSettingItem = find.byType(DatabaseSettingListPopover);
|
||||
final findSettingItem = find.byType(DatabaseSettingsList);
|
||||
final findLayoutButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowyText &&
|
||||
@@ -1148,7 +1227,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
/// Should call [tapDatabaseSettingButton] first.
|
||||
Future<void> tapDatabaseLayoutButton() async {
|
||||
final findSettingItem = find.byType(DatabaseSettingListPopover);
|
||||
final findSettingItem = find.byType(DatabaseSettingsList);
|
||||
final findLayoutButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowyText &&
|
||||
@@ -1164,7 +1243,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> tapCalendarLayoutSettingButton() async {
|
||||
final findSettingItem = find.byType(DatabaseSettingListPopover);
|
||||
final findSettingItem = find.byType(DatabaseSettingsList);
|
||||
final findLayoutButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowyText &&
|
||||
@@ -1242,12 +1321,14 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle(const Duration(milliseconds: 300));
|
||||
}
|
||||
|
||||
Future<void> hoverOnTodayCalendarCell() async {
|
||||
Future<void> hoverOnTodayCalendarCell({
|
||||
Future<void> Function()? onHover,
|
||||
}) async {
|
||||
final todayCell = find.byWidgetPredicate(
|
||||
(widget) => widget is CalendarDayCard && widget.isToday,
|
||||
);
|
||||
|
||||
await hoverOnWidget(todayCell);
|
||||
await hoverOnWidget(todayCell, onHover: onHover);
|
||||
}
|
||||
|
||||
Future<void> tapAddCalendarEventButton() async {
|
||||
@@ -1355,10 +1436,10 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> dragDropRescheduleCalendarEvent(DateTime startDate) async {
|
||||
Future<void> dragDropRescheduleCalendarEvent() async {
|
||||
final findEventCard = find.byType(EventCard);
|
||||
await drag(findEventCard.first, const Offset(0, 300));
|
||||
await pumpAndSettle();
|
||||
await pumpAndSettle(const Duration(microseconds: 300));
|
||||
}
|
||||
|
||||
Future<void> openUnscheduledEventsPopup() async {
|
||||
@@ -1381,17 +1462,97 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(unscheduledEvent);
|
||||
}
|
||||
|
||||
Future<void> tapCreateLinkedDatabaseViewButton(AddButtonAction action) async {
|
||||
Future<void> tapCreateLinkedDatabaseViewButton(
|
||||
DatabaseLayoutPB layoutType,
|
||||
) async {
|
||||
final findAddButton = find.byType(AddDatabaseViewButton);
|
||||
await tapButton(findAddButton);
|
||||
|
||||
final findCreateButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is TarBarAddButtonActionCell && widget.action == action,
|
||||
widget is TabBarAddButtonActionCell && widget.action == layoutType,
|
||||
);
|
||||
await tapButton(findCreateButton);
|
||||
}
|
||||
|
||||
void assertNumberOfGroups(int number) {
|
||||
final groups = find.byType(BoardColumnHeader, skipOffstage: false);
|
||||
expect(groups, findsNWidgets(number));
|
||||
}
|
||||
|
||||
Future<void> scrollBoardToEnd() async {
|
||||
final scrollable = find
|
||||
.descendant(
|
||||
of: find.byType(AppFlowyBoard),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) => widget is Scrollable && widget.axis == Axis.horizontal,
|
||||
),
|
||||
)
|
||||
.first;
|
||||
await scrollUntilVisible(
|
||||
find.byType(BoardTrailing),
|
||||
300,
|
||||
scrollable: scrollable,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> tapNewGroupButton() async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(BoardTrailing),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svg == FlowySvgs.add_s,
|
||||
),
|
||||
);
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
void assertNewGroupTextField(bool isVisible) {
|
||||
final textField = find.descendant(
|
||||
of: find.byType(BoardTrailing),
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
if (isVisible) {
|
||||
expect(textField, findsOneWidget);
|
||||
} else {
|
||||
expect(textField, findsNothing);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> enterNewGroupName(String name, {required bool submit}) async {
|
||||
final textField = find.descendant(
|
||||
of: find.byType(BoardTrailing),
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await enterText(textField, name);
|
||||
await pumpAndSettle();
|
||||
if (submit) {
|
||||
await testTextInput.receiveAction(TextInputAction.done);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> clearNewGroupTextField() async {
|
||||
final textField = find.descendant(
|
||||
of: find.byType(BoardTrailing),
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await tapButton(
|
||||
find.descendant(
|
||||
of: textField,
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowySvg && widget.svg == FlowySvgs.close_filled_m,
|
||||
),
|
||||
),
|
||||
);
|
||||
final textFieldWidget = widget<TextField>(textField);
|
||||
assert(
|
||||
textFieldWidget.controller != null &&
|
||||
textFieldWidget.controller!.text.isEmpty,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> tapTabBarLinkedViewByViewName(String name) async {
|
||||
final viewButton = findTabBarLinkViewByViewName(name);
|
||||
await tapButton(viewButton);
|
||||
@@ -1457,7 +1618,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(okButton);
|
||||
}
|
||||
|
||||
Future<void> assertCurrentDatabaseTagIs(DatabaseLayoutPB layout) async {
|
||||
void assertCurrentDatabaseTagIs(DatabaseLayoutPB layout) {
|
||||
switch (layout) {
|
||||
case DatabaseLayoutPB.Board:
|
||||
expect(find.byType(BoardPage), findsOneWidget);
|
||||
@@ -1476,7 +1637,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
Future<void> selectDatabaseLayoutType(DatabaseLayoutPB layout) async {
|
||||
final findLayoutCell = find.byType(DatabaseViewLayoutCell);
|
||||
final findText = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == layout.layoutName(),
|
||||
(widget) => widget is FlowyText && widget.text == layout.layoutName,
|
||||
);
|
||||
|
||||
final button = find.descendant(
|
||||
@@ -1495,6 +1656,23 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButtonWithName(LocaleKeys.importPanel_database.tr());
|
||||
}
|
||||
|
||||
// Use in edit mode of FieldEditor
|
||||
Future<void> changeNumberFieldFormat() async {
|
||||
final changeFormatButton = find.descendant(
|
||||
of: find.byType(FieldTypeOptionEditor),
|
||||
matching: find.text("Number"),
|
||||
);
|
||||
await tapButton(changeFormatButton);
|
||||
|
||||
await tapButton(
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is NumberFormatCell && widget.format == NumberFormatPB.USD,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Use in edit mode of FieldEditor
|
||||
Future<void> tapAddSelectOptionButton() async {
|
||||
await tapButtonWithName(LocaleKeys.grid_field_addSelectOption.tr());
|
||||
}
|
||||
@@ -1528,24 +1706,45 @@ Finder finderForDatabaseLayoutType(DatabaseLayoutPB layout) {
|
||||
Finder finderForFieldType(FieldType fieldType) {
|
||||
switch (fieldType) {
|
||||
case FieldType.Checkbox:
|
||||
return find.byType(GridCheckboxCell, skipOffstage: false);
|
||||
return find.byType(EditableCheckboxCell, skipOffstage: false);
|
||||
case FieldType.DateTime:
|
||||
return find.byType(GridDateCell, skipOffstage: false);
|
||||
return find.byType(EditableDateCell, skipOffstage: false);
|
||||
case FieldType.LastEditedTime:
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is EditableTimestampCell &&
|
||||
widget.fieldType == FieldType.LastEditedTime,
|
||||
skipOffstage: false,
|
||||
);
|
||||
case FieldType.CreatedTime:
|
||||
return find.byType(GridTimestampCell, skipOffstage: false);
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is EditableTimestampCell &&
|
||||
widget.fieldType == FieldType.CreatedTime,
|
||||
skipOffstage: false,
|
||||
);
|
||||
case FieldType.SingleSelect:
|
||||
return find.byType(GridSingleSelectCell, skipOffstage: false);
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is EditableSelectOptionCell &&
|
||||
widget.fieldType == FieldType.SingleSelect,
|
||||
skipOffstage: false,
|
||||
);
|
||||
case FieldType.MultiSelect:
|
||||
return find.byType(GridMultiSelectCell, skipOffstage: false);
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is EditableSelectOptionCell &&
|
||||
widget.fieldType == FieldType.MultiSelect,
|
||||
skipOffstage: false,
|
||||
);
|
||||
case FieldType.Checklist:
|
||||
return find.byType(GridChecklistCell, skipOffstage: false);
|
||||
return find.byType(EditableChecklistCell, skipOffstage: false);
|
||||
case FieldType.Number:
|
||||
return find.byType(GridNumberCell, skipOffstage: false);
|
||||
return find.byType(EditableNumberCell, skipOffstage: false);
|
||||
case FieldType.RichText:
|
||||
return find.byType(GridTextCell, skipOffstage: false);
|
||||
return find.byType(EditableTextCell, skipOffstage: false);
|
||||
case FieldType.URL:
|
||||
return find.byType(GridURLCell, skipOffstage: false);
|
||||
return find.byType(EditableURLCell, skipOffstage: false);
|
||||
default:
|
||||
throw Exception('Unknown field type: $fieldType');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import 'dart:io';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:archive/archive.dart';
|
||||
|
||||
Future<void> deleteDirectoriesWithSameBaseNameAsPrefix(
|
||||
String path,
|
||||
) async {
|
||||
final dir = Directory(path);
|
||||
final prefix = p.basename(dir.path);
|
||||
final parentDir = dir.parent;
|
||||
|
||||
// Check if the directory exists
|
||||
if (!await parentDir.exists()) {
|
||||
// ignore: avoid_print
|
||||
print('Directory does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
// List all entities in the directory
|
||||
await for (final entity in parentDir.list()) {
|
||||
// Check if the entity is a directory and starts with the specified prefix
|
||||
if (entity is Directory && p.basename(entity.path).startsWith(prefix)) {
|
||||
try {
|
||||
await entity.delete(recursive: true);
|
||||
} catch (e) {
|
||||
// ignore: avoid_print
|
||||
print('Failed to delete directory: ${entity.path}, Error: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> unzipFile(File zipFile, Directory targetDirectory) async {
|
||||
// Read the Zip file from disk.
|
||||
final bytes = zipFile.readAsBytesSync();
|
||||
|
||||
// Decode the Zip file
|
||||
final archive = ZipDecoder().decodeBytes(bytes);
|
||||
|
||||
// Extract the contents of the Zip archive to disk.
|
||||
for (final file in archive) {
|
||||
final filename = file.name;
|
||||
if (file.isFile) {
|
||||
final data = file.content as List<int>;
|
||||
File(p.join(targetDirectory.path, filename))
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(data);
|
||||
} else {
|
||||
Directory(p.join(targetDirectory.path, filename))
|
||||
.createSync(recursive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_skin_tone.dart';
|
||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_add_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/custom_cover_picker.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_popover.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/inline_actions/widgets/inline_actions_handler.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'util.dart';
|
||||
@@ -54,7 +58,27 @@ class EditorOperations {
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_plugins_cover_addIcon.tr(),
|
||||
);
|
||||
expect(find.byType(EmojiPopover), findsOneWidget);
|
||||
expect(find.byType(FlowyEmojiPicker), findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> tapGettingStartedIcon() async {
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(DocumentHeaderNodeWidget),
|
||||
matching: find.findTextInFlowyText('⭐️'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Taps on the 'Skin tone' button
|
||||
///
|
||||
/// Must call [tapAddIconButton] first.
|
||||
Future<void> changeEmojiSkinTone(EmojiSkinTone skinTone) async {
|
||||
await tester.tapButton(
|
||||
find.byTooltip(LocaleKeys.emoji_selectSkinTone.tr()),
|
||||
);
|
||||
final skinToneButton = find.byKey(emojiSkinToneKey(skinTone.icon));
|
||||
await tester.tapButton(skinToneButton);
|
||||
}
|
||||
|
||||
/// Taps the 'Remove Icon' button in the cover toolbar and the icon popover
|
||||
@@ -62,7 +86,10 @@ class EditorOperations {
|
||||
Finder button =
|
||||
find.text(LocaleKeys.document_plugins_cover_removeIcon.tr());
|
||||
if (isInPicker) {
|
||||
button = find.descendant(of: find.byType(EmojiPopover), matching: button);
|
||||
button = find.descendant(
|
||||
of: find.byType(FlowyIconPicker),
|
||||
matching: button,
|
||||
);
|
||||
}
|
||||
|
||||
await tester.tapButton(button);
|
||||
@@ -95,19 +122,25 @@ class EditorOperations {
|
||||
}
|
||||
|
||||
Future<void> addNetworkImageCover(String imageUrl) async {
|
||||
final findNewImageButton = find.byType(NewCustomCoverButton);
|
||||
await tester.tapButton(findNewImageButton);
|
||||
final embedLinkButton = find.findTextInFlowyText(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
);
|
||||
await tester.tapButton(embedLinkButton);
|
||||
|
||||
final imageUrlTextField = find.descendant(
|
||||
of: find.byType(NetworkImageUrlInput),
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await tester.enterText(imageUrlTextField, imageUrl);
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_plugins_cover_add.tr(),
|
||||
);
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_plugins_cover_saveToGallery.tr(),
|
||||
final textField = tester.widget<TextField>(imageUrlTextField);
|
||||
textField.controller?.text = imageUrl;
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.findTextInFlowyText(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -143,7 +176,7 @@ class EditorOperations {
|
||||
await tester.ime.insertCharacter('/');
|
||||
}
|
||||
|
||||
/// trigger the slash command (selection menu)
|
||||
/// trigger the mention (@) command
|
||||
Future<void> showAtMenu() async {
|
||||
await tester.ime.insertCharacter('@');
|
||||
}
|
||||
@@ -170,9 +203,11 @@ class EditorOperations {
|
||||
/// Update the editor's selection
|
||||
Future<void> updateSelection(Selection selection) async {
|
||||
final editorState = getCurrentEditorState();
|
||||
editorState.updateSelectionWithReason(
|
||||
selection,
|
||||
reason: SelectionUpdateReason.uiEvent,
|
||||
unawaited(
|
||||
editorState.updateSelectionWithReason(
|
||||
selection,
|
||||
reason: SelectionUpdateReason.uiEvent,
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
|
||||