Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12198dddb7 | ||
|
|
5efdd6e8b9 | ||
|
|
d1c867d089 | ||
|
|
22b108df70 | ||
|
|
aff9a61919 | ||
|
|
043cd3f3bb | ||
|
|
29fb4af40a | ||
|
|
a2e211555e | ||
|
|
d1af172fb7 | ||
|
|
23b6f94e82 | ||
|
|
82fffba45a | ||
|
|
0373088fb8 | ||
|
|
0da0f320b7 | ||
|
|
4abdcd9478 | ||
|
|
1e54c2dc7c | ||
|
|
01747f13e8 | ||
|
|
4a5eda6eeb | ||
|
|
b5d799655a | ||
|
|
43b250ec28 | ||
|
|
20e82880ff | ||
|
|
661a0879c6 | ||
|
|
2fb18dd051 | ||
|
|
6334255e15 | ||
|
|
f66821715f | ||
|
|
620e027c3e | ||
|
|
864768b3ba | ||
|
|
a8b4f22703 | ||
|
|
432db0f6d5 | ||
|
|
e8e4162a5c | ||
|
|
241234726f | ||
|
|
e426970eed | ||
|
|
d5544af6c5 | ||
|
|
9361afd573 | ||
|
|
dc93a336d6 | ||
|
|
4e5482488f | ||
|
|
5bbf174ffd | ||
|
|
f36e3ae378 | ||
|
|
dc6349b4b1 | ||
|
|
44d8def3ce | ||
|
|
c6ad57f11d | ||
|
|
ff23165d3e | ||
|
|
253e7597c4 | ||
|
|
d2e3fdfefd | ||
|
|
a2336f5bf7 | ||
|
|
8833df1740 | ||
|
|
1524b10a8a | ||
|
|
fe0fa9b530 | ||
|
|
80afcf44c0 | ||
|
|
e500c89978 | ||
|
|
079b9888a8 | ||
|
|
0fe383e538 | ||
|
|
c006e29afc | ||
|
|
655a2b7093 | ||
|
|
23c67bcdba | ||
|
|
521fffd97c | ||
|
|
2ecc2a67a9 | ||
|
|
2d11215bb2 | ||
|
|
6d0c9f766b | ||
|
|
7a3f013a87 | ||
|
|
8c1520b273 | ||
|
|
a7b850e752 | ||
|
|
42dc6017c6 | ||
|
|
f9fba6b561 | ||
|
|
46896b5a2b | ||
|
|
2b8dca209e | ||
|
|
c78f23e1c0 | ||
|
|
50f5be3e75 | ||
|
|
e1c68c1b72 | ||
|
|
3bcadff152 | ||
|
|
b09ff040f1 | ||
|
|
bf087bba5d | ||
|
|
605a53f2ae | ||
|
|
ecc5555a4e | ||
|
|
d2a5bab6d9 | ||
|
|
a1dec0f269 | ||
|
|
b24fbc6b60 | ||
|
|
bbb3f95a13 | ||
|
|
7859fc7922 | ||
|
|
f812040f04 | ||
|
|
7d96c2dfd4 | ||
|
|
e4132ed217 | ||
|
|
a7f40b2adc | ||
|
|
c996c9c28e | ||
|
|
f59b8a2e5c | ||
|
|
f7480cba16 | ||
|
|
271b250eac | ||
|
|
6b1e7b6ac8 | ||
|
|
bf2a00b133 | ||
|
|
a8ed93054c | ||
|
|
b9ad2768cf | ||
|
|
54c9d12171 | ||
|
|
40312f4260 | ||
|
|
d2ca41c8f5 | ||
|
|
7586a0ed48 | ||
|
|
d8b32168a9 | ||
|
|
8ca541bfa1 | ||
|
|
b64da2c02f | ||
|
|
fdaca36b87 | ||
|
|
8557383a89 | ||
|
|
fa86480458 | ||
|
|
d75eb05707 | ||
|
|
a2b32585a1 | ||
|
|
75cea400d2 | ||
|
|
ed82ec8eef | ||
|
|
b6debd6c71 | ||
|
|
a05ed63a3e | ||
|
|
3e75f1f24a | ||
|
|
e607694729 | ||
|
|
91d870415e | ||
|
|
21a497924c | ||
|
|
4a126e17ce | ||
|
|
8bf97ad5c6 | ||
|
|
a85d11fb6d | ||
|
|
1fd34bc58a | ||
|
|
70a7b856a2 | ||
|
|
49683e6079 | ||
|
|
13107b63e9 | ||
|
|
ce1a6e4fca | ||
|
|
1d23e28eaf | ||
|
|
2a4d78b688 | ||
|
|
cbe452a73d | ||
|
|
27899ee993 | ||
|
|
785597f53e | ||
|
|
e2ce274718 | ||
|
|
6be9c00127 | ||
|
|
aa621a8d84 | ||
|
|
2d4300e931 | ||
|
|
dc12938ab6 | ||
|
|
2d674060c6 | ||
|
|
ecca81f3b8 | ||
|
|
15329b39d0 | ||
|
|
bd5f5f8b9e | ||
|
|
4f4be7eac7 | ||
|
|
f1a4deb459 | ||
|
|
4708c0f779 | ||
|
|
3d7a500550 | ||
|
|
815c99710e | ||
|
|
c9fe93920f | ||
|
|
5012b99c9b | ||
|
|
6b46372aa0 | ||
|
|
b6d873db1b | ||
|
|
38d2bd7ee8 | ||
|
|
bb3e9d5bd8 | ||
|
|
94060a0a99 | ||
|
|
86696b271e | ||
|
|
76fde00cc4 | ||
|
|
b794f3894e | ||
|
|
3b72f90ca5 | ||
|
|
d73e388d01 | ||
|
|
383e06ff2d | ||
|
|
ff93fbf0e6 | ||
|
|
bd7977d8ba | ||
|
|
6e7d044208 | ||
|
|
d8ca693976 | ||
|
|
52e7fb49cd | ||
|
|
f6e33a59d5 | ||
|
|
a0e981ec6b | ||
|
|
041e3c155a | ||
|
|
e4eff7e632 | ||
|
|
9f66dcdc8f | ||
|
|
890ac5a213 | ||
|
|
1cecfae6f4 | ||
|
|
1757570337 | ||
|
|
57d4652824 | ||
|
|
1e485188eb | ||
|
|
03e8dba5f3 | ||
|
|
d5cfd054cc | ||
|
|
9c9168ac5b | ||
|
|
4762d69851 | ||
|
|
aec7bc847e | ||
|
|
4d42c9ea68 | ||
|
|
0d64aa4311 | ||
|
|
2a59579c17 | ||
|
|
4ad7c48b25 | ||
|
|
2c0cdfa6c5 | ||
|
|
8b04506ac4 | ||
|
|
a1395cb54a | ||
|
|
526de2eb5c | ||
|
|
e40e1e9a8a | ||
|
|
ace729eb78 | ||
|
|
189f0e4b58 | ||
|
|
e8afcbb828 | ||
|
|
42e83b3ce9 | ||
|
|
859eaf903b | ||
|
|
0bfe071caf | ||
|
|
b8b7a10b33 | ||
|
|
cb44a885a1 | ||
|
|
083be32fa6 | ||
|
|
8b6575d1ee | ||
|
|
a8f136eda2 | ||
|
|
13b3439bd6 | ||
|
|
13a35672ab | ||
|
|
857b3aa106 | ||
|
|
a0139dd475 | ||
|
|
9a5dbbb3ce | ||
|
|
acae34836e | ||
|
|
68c4e19f91 | ||
|
|
b2978e0d6c | ||
|
|
b7bc847107 | ||
|
|
aa07393253 | ||
|
|
e85dbe724c | ||
|
|
24b3d69860 | ||
|
|
c67e266174 | ||
|
|
d303d5affd | ||
|
|
e6d166df3b | ||
|
|
9b7ee4b978 | ||
|
|
0c0bd54f52 | ||
|
|
f1b035e362 | ||
|
|
bd62a161be | ||
|
|
7e83d598f8 | ||
|
|
bfc6cd721e | ||
|
|
3c6ce4ea4d | ||
|
|
c1e7e72154 | ||
|
|
759998cacb | ||
|
|
a6467c1b16 | ||
|
|
edfd1ab116 | ||
|
|
c3c99c7960 | ||
|
|
3bead0bab9 | ||
|
|
eb0c9d3264 | ||
|
|
5c7ba0466f | ||
|
|
b1177ad3ea | ||
|
|
b9faf3b24a | ||
|
|
12ff548b09 | ||
|
|
027ab2c1ef | ||
|
|
8273d66c50 | ||
|
|
38fa9f7942 | ||
|
|
39f8c47758 | ||
|
|
4cfd83cbc4 | ||
|
|
cdcb393efd | ||
|
|
a0ed043cb8 | ||
|
|
f47c88b022 | ||
|
|
c732438f8a | ||
|
|
f3b8ec8a04 | ||
|
|
a490f34a61 | ||
|
|
28a27d1b67 | ||
|
|
6edb184bfb | ||
|
|
dbbdc13d96 | ||
|
|
eeddf341e1 | ||
|
|
aaa72fc143 | ||
|
|
6220680ce0 | ||
|
|
b4279f8004 | ||
|
|
dc813d85a8 | ||
|
|
aa4fe2ba50 | ||
|
|
a5b5a1f679 | ||
|
|
287c69a4fb | ||
|
|
c4f6703b76 | ||
|
|
3bdc8c222c | ||
|
|
560529f7d9 | ||
|
|
9f44ae959a | ||
|
|
b2c366f6ae | ||
|
|
3a9de61599 | ||
|
|
266a2a53ab | ||
|
|
d52042fa4f | ||
|
|
a69e83c2cb | ||
|
|
999ffeba21 | ||
|
|
cb15018825 | ||
|
|
22e89fd197 | ||
|
|
554c821db9 | ||
|
|
2eb88821ca | ||
|
|
813d024810 | ||
|
|
401ce7dfea | ||
|
|
cadb177acd | ||
|
|
016e131bd3 | ||
|
|
7831d8d4ab | ||
|
|
e1e8747f15 | ||
|
|
6bfac6b80a | ||
|
|
a971f3c6d2 | ||
|
|
1d73174b0c | ||
|
|
10a1571910 | ||
|
|
4d860cc404 | ||
|
|
9a8109f5f8 | ||
|
|
4981baac13 | ||
|
|
f3544375c9 | ||
|
|
33802fa62d | ||
|
|
6d0598b101 | ||
|
|
e0d6b194bf | ||
|
|
9135fb94ad | ||
|
|
969726ef73 | ||
|
|
3c446d5e78 | ||
|
|
3fce5eebf8 | ||
|
|
119fb03342 | ||
|
|
0b94e2b25d | ||
|
|
044dad1d3e | ||
|
|
d4278a7549 | ||
|
|
d467dcd68a | ||
|
|
1ad351195d | ||
|
|
122a392bf8 | ||
|
|
d86afb03ba | ||
|
|
cc66147bc0 |
No files matched your search
@@ -63,7 +63,7 @@ runs:
|
||||
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
|
||||
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 libmpv-dev mpv
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
|
||||
@@ -52,7 +52,7 @@ runs:
|
||||
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 network-manager
|
||||
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 network-manager libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
# env:
|
||||
# CARGO_TERM_COLOR: always
|
||||
# FLUTTER_VERSION: "3.19.0"
|
||||
# FLUTTER_VERSION: "3.22.0"
|
||||
# RUST_TOOLCHAIN: "1.77.2"
|
||||
# CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
name: Build Bot
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
dispatch_slash_command:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# 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 }}
|
||||
@@ -1,72 +0,0 @@
|
||||
name: Deploy Web (Test)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- build/test
|
||||
env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.WEB_TEST_SSH_PRIVATE_KEY }}
|
||||
REMOTE_HOST: ${{ secrets.WEB_TEST_REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ secrets.WEB_TEST_REMOTE_USER }}
|
||||
SSL_CERTIFICATE: ${{ secrets.WEB_TEST_SSL_CERTIFICATE }}
|
||||
SSL_CERTIFICATE_KEY: ${{ secrets.WEB_TEST_SSL_CERTIFICATE_KEY }}
|
||||
ENV_FILE: test.env
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
- name: Node_modules cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: frontend/appflowy_web_app/node_modules
|
||||
key: node-modules-${{ runner.os }}
|
||||
- name: install frontend dependencies
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm install
|
||||
- name: copy env file
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
cp ${{ env.ENV_FILE }} .env
|
||||
- name: test and lint
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run lint
|
||||
- name: build
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run build
|
||||
- name: generate SSL certificate
|
||||
run: |
|
||||
echo "${{ env.SSL_CERTIFICATE }}" > nginx-signed.crt
|
||||
echo "${{ env.SSL_CERTIFICATE_KEY }}" > nginx-signed.key
|
||||
- name: Deploy to EC2
|
||||
uses: easingthemes/ssh-deploy@main
|
||||
with:
|
||||
SSH_PRIVATE_KEY: ${{ env.SSH_PRIVATE_KEY }}
|
||||
ARGS: "-rlgoDzvc -i"
|
||||
SOURCE: "frontend/appflowy_web_app/dist frontend/appflowy_web_app/Dockerfile frontend/appflowy_web_app/nginx.conf frontend/appflowy_web_app/.env nginx-signed.crt nginx-signed.key"
|
||||
REMOTE_HOST: ${{ env.REMOTE_HOST }}
|
||||
REMOTE_USER: ${{ env.REMOTE_USER }}
|
||||
EXCLUDE: "frontend/appflowy_web_app/dist/, frontend/appflowy_web_app/node_modules/"
|
||||
SCRIPT_AFTER: |
|
||||
docker build -t appflowy-web-app .
|
||||
docker rm -f appflowy-web-app || true
|
||||
docker run -d -p 80:80 -p 443:443 --name appflowy-web-app appflowy-web-app
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
- release/*
|
||||
paths:
|
||||
- frontend/**
|
||||
types: [opened, synchronize, reopened, unlocked, ready_for_review]
|
||||
types: [ opened, synchronize, reopened, unlocked, ready_for_review ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
|
||||
@@ -25,7 +25,7 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
|
||||
@@ -89,6 +89,7 @@ jobs:
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
flutter_version: ${{ env.FLUTTER_VERSION }}
|
||||
DISABLE_CI_TEST_LOG: "true"
|
||||
rust_toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
cargo_make_version: ${{ env.CARGO_MAKE_VERSION }}
|
||||
rust_target: ${{ matrix.target }}
|
||||
@@ -172,7 +173,7 @@ jobs:
|
||||
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
|
||||
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 libmpv-dev mpv
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
@@ -202,6 +203,7 @@ jobs:
|
||||
- name: Run Flutter unit tests
|
||||
env:
|
||||
DISABLE_EVENT_LOG: true
|
||||
DISABLE_CI_TEST_LOG: "true"
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "macOS" ]; then
|
||||
@@ -246,10 +248,13 @@ jobs:
|
||||
env:
|
||||
BACKEND_VERSION: 0.3.24-amd64
|
||||
run: |
|
||||
docker compose down -v --remove-orphans
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
if [ "$(docker ps --filter name=appflowy-cloud -q)" == "" ]; then
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
else
|
||||
echo "Docker container 'appflowy-cloud' is already running."
|
||||
fi
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
@@ -272,7 +277,7 @@ jobs:
|
||||
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
|
||||
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 libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Enable Flutter Desktop
|
||||
@@ -319,6 +324,12 @@ jobs:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install video dependency
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Flutter Integration Test 1
|
||||
uses: ./.github/actions/flutter_integration_test
|
||||
with:
|
||||
@@ -343,6 +354,12 @@ jobs:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install video dependency
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Flutter Integration Test 2
|
||||
uses: ./.github/actions/flutter_integration_test
|
||||
with:
|
||||
@@ -367,6 +384,12 @@ jobs:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install video dependency
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libmpv-dev mpv
|
||||
shell: bash
|
||||
|
||||
- name: Flutter Integration Test 3
|
||||
uses: ./.github/actions/flutter_integration_test
|
||||
with:
|
||||
|
||||
@@ -8,6 +8,7 @@ on:
|
||||
- ".github/workflows/mobile_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
- "!frontend/appflowy_web_app/**"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -16,9 +17,10 @@ on:
|
||||
- ".github/workflows/mobile_ci.yaml"
|
||||
- "frontend/**"
|
||||
- "!frontend/appflowy_tauri/**"
|
||||
- "!frontend/appflowy_web_app/**"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
|
||||
concurrency:
|
||||
@@ -26,20 +28,35 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ macos-14 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
build-self-hosted:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: self-hosted
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo make --profile development-ios-arm64-sim appflowy-core-dev-ios
|
||||
cargo make --profile development-ios-arm64-sim code_generation
|
||||
|
||||
- uses: futureware-tech/simulator-action@v3
|
||||
id: simulator-action
|
||||
with:
|
||||
model: 'iPhone 15'
|
||||
shutdown_after_job: false
|
||||
|
||||
build-macos:
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
@@ -47,8 +64,7 @@ jobs:
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Install flutter
|
||||
id: flutter
|
||||
- name: Install Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
@@ -57,7 +73,7 @@ jobs:
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: ${{ matrix.os }}
|
||||
prefix-key: macos-latest
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
@@ -86,7 +102,16 @@ jobs:
|
||||
model: 'iPhone 15'
|
||||
shutdown_after_job: false
|
||||
|
||||
# enable it again if the 12 mins timeout is fixed
|
||||
# - name: Run integration tests
|
||||
# working-directory: frontend/appflowy_flutter
|
||||
# run: flutter test integration_test/runner.dart -d ${{ steps.simulator-action.outputs.udid }}
|
||||
# - name: Run AppFlowy on simulator
|
||||
# working-directory: frontend/appflowy_flutter
|
||||
# run: |
|
||||
# flutter run -d ${{ steps.simulator-action.outputs.udid }} &
|
||||
# pid=$!
|
||||
# sleep 500
|
||||
# kill $pid
|
||||
# continue-on-error: true
|
||||
|
||||
# enable it again if the 12 mins timeout is fixed
|
||||
# - name: Run integration tests
|
||||
# working-directory: frontend/appflowy_flutter
|
||||
# run: flutter test integration_test/runner.dart -d ${{ steps.simulator-action.outputs.udid }}
|
||||
@@ -6,7 +6,7 @@ on:
|
||||
- "*"
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
|
||||
jobs:
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
job:
|
||||
- { target: x86_64-apple-darwin, os: macos-11, extra-build-args: "" }
|
||||
- { target: x86_64-apple-darwin, os: macos-12, extra-build-args: "" }
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
@@ -233,7 +233,7 @@ jobs:
|
||||
job:
|
||||
- {
|
||||
targets: "aarch64-apple-darwin,x86_64-apple-darwin",
|
||||
os: macos-11,
|
||||
os: macos-latest,
|
||||
extra-build-args: "",
|
||||
}
|
||||
steps:
|
||||
@@ -367,8 +367,8 @@ jobs:
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
|
||||
sudo apt-get install keybinder-3.0 libnotify-dev
|
||||
sudo apt-get -y install alien
|
||||
sudo apt-get install keybinder-3.0
|
||||
sudo apt-get install -y alien libnotify-dev libmpv-dev mpv
|
||||
source $HOME/.cargo/env
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
@@ -479,6 +479,24 @@ jobs:
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache
|
||||
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max
|
||||
|
||||
notify-failure:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-for-macOS-x86_64
|
||||
- build-for-windows
|
||||
- build-for-linux
|
||||
if: failure()
|
||||
steps:
|
||||
- uses: 8398a7/action-slack@v3
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
text: |
|
||||
🔴🔴🔴Workflow ${{ github.workflow }} in repository ${{ github.repository }} was failed 🔴🔴🔴.
|
||||
fields: repo,message,author,eventName,ref,workflow
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.RELEASE_SLACK_WEBHOOK }}
|
||||
if: always()
|
||||
|
||||
notify-discord:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
|
||||
@@ -8,6 +8,7 @@ on:
|
||||
- "release/*"
|
||||
paths:
|
||||
- "frontend/rust-lib/**"
|
||||
- ".github/workflows/rust_ci.yaml"
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -22,46 +23,71 @@ env:
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
|
||||
jobs:
|
||||
test-on-ubuntu:
|
||||
self-hosted-job:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout Appflowy Cloud
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: AppFlowy-IO/AppFlowy-Cloud
|
||||
path: AppFlowy-Cloud
|
||||
|
||||
- name: Prepare Appflowy Cloud env
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
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 rust-lib tests
|
||||
working-directory: frontend/rust-lib
|
||||
env:
|
||||
RUST_LOG: info
|
||||
RUST_BACKTRACE: 1
|
||||
af_cloud_test_base_url: http://localhost
|
||||
af_cloud_test_ws_url: ws://localhost/ws/v1
|
||||
af_cloud_test_gotrue_url: http://localhost/gotrue
|
||||
run: |
|
||||
DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="dart"
|
||||
|
||||
- name: rustfmt rust-lib
|
||||
run: cargo fmt --all -- --check
|
||||
working-directory: frontend/rust-lib/
|
||||
|
||||
- name: clippy rust-lib
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
working-directory: frontend/rust-lib
|
||||
|
||||
ubuntu-job:
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# - name: Maximize build space
|
||||
# uses: easimon/maximize-build-space@master
|
||||
# with:
|
||||
# root-reserve-mb: 2048
|
||||
# swap-size-mb: 1024
|
||||
# remove-dotnet: 'true'
|
||||
|
||||
# # the following step is required to avoid running out of space
|
||||
# - name: Maximize build space
|
||||
# run: |
|
||||
# sudo rm -rf /usr/share/dotnet
|
||||
# sudo rm -rf /opt/ghc
|
||||
# sudo rm -rf "/usr/local/share/boost"
|
||||
# sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
# sudo docker image prune --all --force
|
||||
- name: Maximize build space
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
sudo rm -rf "/usr/local/share/boost"
|
||||
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
|
||||
sudo docker image prune --all --force
|
||||
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
id: rust_toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
profile: minimal
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo install --force duckscript_cli
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: "ubuntu-latest"
|
||||
prefix-key: ${{ runner.os }}
|
||||
cache-on-failure: true
|
||||
workspaces: |
|
||||
frontend/rust-lib
|
||||
|
||||
@@ -74,18 +100,20 @@ jobs:
|
||||
- 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|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
|
||||
- name: Run Docker-Compose
|
||||
working-directory: AppFlowy-Cloud
|
||||
env:
|
||||
BACKEND_VERSION: 0.3.24-amd64
|
||||
run: |
|
||||
docker pull appflowyinc/appflowy_cloud:latest
|
||||
docker compose up -d
|
||||
if [ "$(docker ps --filter name=appflowy-cloud -q)" == "" ]; then
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
sleep 10
|
||||
else
|
||||
echo "Docker container 'appflowy-cloud' is already running."
|
||||
fi
|
||||
|
||||
- name: Run rust-lib tests
|
||||
working-directory: frontend/rust-lib
|
||||
@@ -96,7 +124,7 @@ jobs:
|
||||
af_cloud_test_ws_url: ws://localhost/ws/v1
|
||||
af_cloud_test_gotrue_url: http://localhost/gotrue
|
||||
run: |
|
||||
DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="dart" -- --nocapture
|
||||
DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="dart"
|
||||
|
||||
- name: rustfmt rust-lib
|
||||
run: cargo fmt --all -- --check
|
||||
@@ -106,6 +134,12 @@ jobs:
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
working-directory: frontend/rust-lib
|
||||
|
||||
- name: "Debug: show Appflowy-Cloud container logs"
|
||||
if: failure()
|
||||
working-directory: AppFlowy-Cloud
|
||||
run: |
|
||||
docker compose logs appflowy_cloud
|
||||
|
||||
- name: Clean up Docker images
|
||||
run: |
|
||||
docker image prune -af
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.19.0"
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
name: Tauri2-CI
|
||||
name: Tauri-CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
@@ -11,28 +12,47 @@ env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
CI: true
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tauri-build:
|
||||
if: github.event.pull_request.draft != true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ ubuntu-20.04 ]
|
||||
tauri-build-self-hosted:
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: self-hosted
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
env:
|
||||
CI: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install frontend dependencies
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
mkdir dist
|
||||
pnpm install
|
||||
cd src-tauri && cargo build
|
||||
|
||||
- name: Maximize build space (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
- name: test and lint
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run lint:tauri
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tauriScript: pnpm tauri
|
||||
projectPath: frontend/appflowy_web_app
|
||||
args: "--debug"
|
||||
|
||||
tauri-build-ubuntu:
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Maximize build space
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
@@ -61,36 +81,27 @@ jobs:
|
||||
override: true
|
||||
profile: minimal
|
||||
|
||||
- name: Rust cache
|
||||
uses: swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "./frontend/appflowy_web_app/src-tauri -> target"
|
||||
|
||||
- name: Node_modules cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: frontend/appflowy_web_app/node_modules
|
||||
key: node-modules-${{ runner.os }}
|
||||
|
||||
- name: install dependencies (windows only)
|
||||
if: matrix.platform == 'windows-latest'
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force duckscript_cli
|
||||
vcpkg integrate install
|
||||
|
||||
- name: install dependencies (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
- name: install dependencies
|
||||
working-directory: frontend
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
- name: install cargo-make
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
|
||||
|
||||
- name: install tauri deps tools
|
||||
working-directory: frontend
|
||||
run: |
|
||||
cargo install --force cargo-make
|
||||
cargo make appflowy-tauri-deps-tools
|
||||
shell: bash
|
||||
|
||||
- name: install frontend dependencies
|
||||
working-directory: frontend/appflowy_web_app
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
name: Tauri-CI
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/tauri_ci.yaml"
|
||||
- "frontend/rust-lib/**"
|
||||
- "frontend/appflowy_tauri/**"
|
||||
- "frontend/resources/**"
|
||||
push:
|
||||
branches:
|
||||
- build/tauri
|
||||
|
||||
env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Web2-CI
|
||||
name: Web-CI
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
@@ -17,14 +17,14 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [ ubuntu-latest ]
|
||||
platform: [ ubuntu-20.04 ]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Maximize build space (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
if: matrix.platform == 'ubuntu-20.04'
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
sudo rm -rf /opt/ghc
|
||||
@@ -52,10 +52,11 @@ jobs:
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm install
|
||||
- name: test and lint
|
||||
- name: Run lint check
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run lint
|
||||
|
||||
- name: build and analyze
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
name: WEB-CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build:
|
||||
description: 'Build the web app'
|
||||
required: true
|
||||
default: 'true'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
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@v4
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
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
|
||||
|
||||
# TODO: Can combine caching deps and node_modules in one
|
||||
# See Glob patterns: https://github.com/actions/toolkit/tree/main/packages/glob
|
||||
- name: Cache Node.js dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: npm-${{ runner.os }}
|
||||
|
||||
- name: Cache node_modules
|
||||
uses: actions/cache@v4
|
||||
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
|
||||
@@ -0,0 +1,65 @@
|
||||
name: Web Code Coverage
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/web2_ci.yaml"
|
||||
- "frontend/appflowy_web_app/**"
|
||||
- "frontend/resources/**"
|
||||
|
||||
env:
|
||||
NODE_VERSION: "18.16.0"
|
||||
PNPM_VERSION: "8.5.0"
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
test:
|
||||
if: github.event.pull_request.draft != true
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Maximize build space (ubuntu only)
|
||||
run: |
|
||||
sudo rm -rf /usr/share/dotnet
|
||||
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: setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: ${{ env.PNPM_VERSION }}
|
||||
# Install pnpm dependencies, cache them correctly
|
||||
# and run all Cypress tests
|
||||
- name: Cypress run
|
||||
uses: cypress-io/github-action@v6
|
||||
with:
|
||||
working-directory: frontend/appflowy_web_app
|
||||
component: true
|
||||
build: pnpm run build
|
||||
start: pnpm run start
|
||||
browser: chrome
|
||||
|
||||
- name: Jest run
|
||||
working-directory: frontend/appflowy_web_app
|
||||
run: |
|
||||
pnpm run test:unit
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
token: cf9245e0-e136-4e21-b0ee-35755fa0c493
|
||||
files: frontend/appflowy_web_app/coverage/jest/lcov.info,frontend/appflowy_web_app/coverage/cypress/lcov.info
|
||||
flags: appflowy_web_app
|
||||
name: frontend/appflowy_web_app
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
|
||||
@@ -1,4 +1,88 @@
|
||||
# Release Notes
|
||||
## Version 0.6.5 - 24/07/2024
|
||||
### New Features
|
||||
- Publish a Database to the Web
|
||||
|
||||
## Version 0.6.4 - 16/07/2024
|
||||
### New Features
|
||||
- Enhanced the message style on the AI chat page.
|
||||
- Added the ability to choose cursor color and selection color from a palette in settings page.
|
||||
### Bug Fixes
|
||||
- Optimized the performance for loading recent pages.
|
||||
- Fixed an issue where the cursor would jump randomly when typing in the document title on mobile.
|
||||
|
||||
## Version 0.6.3 - 08/07/2024
|
||||
### New Features
|
||||
- Publish a Document to the Web
|
||||
|
||||
## Version 0.6.2 - 01/07/2024
|
||||
### New Features
|
||||
- Added support for duplicating spaces.
|
||||
- Added support for moving pages across spaces.
|
||||
- Undo markdown formatting with `Ctrl + Z` or `Cmd + Z`.
|
||||
- Improved shortcuts settings UI.
|
||||
### Bug Fixes
|
||||
- Fixed unable to zoom in with `Ctrl` and `+` or `Cmd` and `+` on some keyboards.
|
||||
- Fixed unable to paste nested lists in existing lists.
|
||||
|
||||
## Version 0.6.1 - 22/06/2024
|
||||
### New Features
|
||||
- Introduced the "Space" feature to help you organize your pages more efficiently.
|
||||
### Bug Fixes
|
||||
- Resolved shortcut conflicts on the board page.
|
||||
- Resolved an issue where underscores could cause the editor to freeze.
|
||||
|
||||
## Version 0.6.0 - 19/06/2024
|
||||
### New Features
|
||||
- Introduced the "Space" feature to help you organize your pages more efficiently.
|
||||
### Bug Fixes
|
||||
- Resolved shortcut conflicts on the board page.
|
||||
- Resolved an issue where underscores could cause the editor to freeze.
|
||||
|
||||
## Version 0.5.9 - 06/06/2024
|
||||
### New Features
|
||||
- Revamped the sidebar for both Desktop and Mobile.
|
||||
- Added support for embedding videos in documents.
|
||||
- Introduced a hotkey (Cmd/Ctrl + 0) to reset the app scale.
|
||||
- Supported searching the workspace by page title.
|
||||
### Bug Fixes
|
||||
- Fixed the issue preventing the use of Backspace to delete words in Kanban boards.
|
||||
|
||||
## Version 0.5.8 - 05/20/2024
|
||||
### New Features
|
||||
- Improvement to the Callout block to insert new lines
|
||||
- New settings page "Manage data" replaced the "Files" page
|
||||
- New settings page "Workspace" replaced the "Appearance" and "Language" pages
|
||||
- A custom implementation of a title bar for Windows users
|
||||
- Added support for selecting Cards in kanban and performing grouped keyboard shortcuts
|
||||
- Added support for default system font family
|
||||
- Support for scaling the application up/down using a keyboard shortcut (CMD/CTRL + PLUS/MINUS)
|
||||
|
||||
### Bug Fixes
|
||||
- Resolved and refined the UI on Mobile
|
||||
- Resolved issue with text editing in database
|
||||
- Improved appearance of empty text cells in kanban/calendar
|
||||
- Resolved an issue where a page's more actions (delete, duplicate) did not work properly
|
||||
- Resolved and inconsistency in padding on get started screen on Desktop
|
||||
|
||||
## Version 0.5.7 - 05/10/2024
|
||||
### Bug Fixes
|
||||
- Resolved page opening issue on Android.
|
||||
- Fixed text input inconsistency on Kanban board cards.
|
||||
|
||||
## Version 0.5.6 - 05/07/2024
|
||||
### New Features
|
||||
- Team collaboration is live! Add members to your workspace to edit and collaborate on pages together.
|
||||
- Collaborate in real time on the same page with other members. Edits made by others will appear instantly.
|
||||
- Create multiple workspaces for different kinds of content.
|
||||
- Customize your entire page on mobile through the Page Style menu with options for layout, font, font size, emoji, and cover image.
|
||||
- Open a row record as a full page.
|
||||
### Bug Fixes
|
||||
- Resolved issue with setting background color for the Simple Table block.
|
||||
- Adjusted toolbar for various screen sizes.
|
||||
- Added a request for photo permission before uploading images on mobile.
|
||||
- Exported creation and last modification timestamps to CSV.
|
||||
|
||||
## Version 0.5.5 - 04/24/2024
|
||||
### New Features
|
||||
- Improved the display of code blocks with line numbers
|
||||
@@ -70,7 +154,7 @@
|
||||
- Fixed a bug where newly created rows were not being automatically sorted.
|
||||
- Fixed issues related to deleting a sorting field or sort not removing existing sorts properly.
|
||||
### Notes
|
||||
- Windows 7, Windows 8, and iOS 11 are not yet supported due to the upgrade to Flutter 3.19.0.
|
||||
- Windows 7, Windows 8, and iOS 11 are not yet supported due to the upgrade to Flutter 3.22.0.
|
||||
|
||||
## Version 0.4.9 - 02/17/2024
|
||||
### Bug Fixes
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"type": "dart",
|
||||
"env": {
|
||||
"RUST_LOG": "debug",
|
||||
"RUST_BACKTRACE": "1"
|
||||
},
|
||||
// uncomment the following line to testing performance.
|
||||
// "flutterMode": "profile",
|
||||
@@ -138,4 +137,4 @@
|
||||
"cwd": "${workspaceRoot}/appflowy_tauri/"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
|
||||
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
|
||||
CARGO_MAKE_CRATE_NAME = "dart-ffi"
|
||||
LIB_NAME = "dart_ffi"
|
||||
APPFLOWY_VERSION = "0.5.5"
|
||||
APPFLOWY_VERSION = "0.6.6"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||
@@ -50,7 +50,6 @@ 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"
|
||||
TAURI_APP_BACKEND_SERVICE_PATH = "appflowy_web_app/src/application/services/tauri-services/backend"
|
||||
# Test default config
|
||||
TEST_CRATE_TYPE = "cdylib"
|
||||
|
||||
@@ -52,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 33
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 34
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
multiDexEnabled true
|
||||
|
||||
@@ -47,9 +47,22 @@
|
||||
</application>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<!-- Permission to read files from external storage (outside application container).
|
||||
As of Android 12 this permission no longer has any effect. Instead use the
|
||||
READ_MEDIA_IMAGES, READ_MEDIA_VIDEO or READM_MEDIA_AUDIO permissions. -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||
<!-- Permissions to read media files. -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||
</intent>
|
||||
</queries>
|
||||
<!--
|
||||
Media access permissions.
|
||||
Android 13 or higher.
|
||||
Used for VideoBlock (edia_kit)
|
||||
-->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
</manifest>
|
||||
@@ -11,6 +11,8 @@ const uint8_t *sync_event(const uint8_t *input, uintptr_t len);
|
||||
|
||||
int32_t set_stream_port(int64_t port);
|
||||
|
||||
int32_t set_log_stream_port(int64_t port);
|
||||
|
||||
void link_me_please(void);
|
||||
|
||||
void rust_log(int64_t level, const char *data);
|
||||
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 731 KiB |
|
After Width: | Height: | Size: 465 KiB |
|
After Width: | Height: | Size: 526 KiB |
|
After Width: | Height: | Size: 293 KiB |
|
After Width: | Height: | Size: 765 KiB |
@@ -3,21 +3,28 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.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/pages/settings_account_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.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:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../desktop/board/board_hide_groups_test.dart';
|
||||
import '../shared/dir.dart';
|
||||
import '../shared/mock/mock_file_picker.dart';
|
||||
import '../shared/util.dart';
|
||||
@@ -35,24 +42,37 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// reanme the name of the anon user
|
||||
// rename 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.openSettingsPage(SettingsPage.account);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterUserName('local_user');
|
||||
|
||||
// Scroll to sign-in
|
||||
await tester.scrollUntilVisible(
|
||||
find.byType(SignInOutButton),
|
||||
100,
|
||||
scrollable: find.findSettingsScrollable(),
|
||||
);
|
||||
|
||||
await tester.tapButton(find.byType(SignInOutButton));
|
||||
|
||||
// sign up with Google
|
||||
await tester.tapGoogleLoginInButton();
|
||||
|
||||
// sign out
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
|
||||
// Scroll to sign-out
|
||||
await tester.scrollUntilVisible(
|
||||
find.byType(SignInOutButton),
|
||||
100,
|
||||
scrollable: find.findSettingsScrollable(),
|
||||
);
|
||||
|
||||
await tester.logout();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
@@ -63,8 +83,9 @@ void main() {
|
||||
|
||||
// New anon user name
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
final userNameInput = tester.widget(userNameFinder) as UserNameInput;
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
final userNameInput =
|
||||
tester.widget(find.byType(UserProfileSetting)) as UserProfileSetting;
|
||||
expect(userNameInput.name, 'Me');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,8 +6,8 @@ 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/pages/settings_account_view.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';
|
||||
@@ -37,14 +37,21 @@ void main() {
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.tapButton(find.byType(SettingLogoutButton));
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
|
||||
// Scroll to sign-out
|
||||
await tester.scrollUntilVisible(
|
||||
find.byType(SignInOutButton),
|
||||
100,
|
||||
scrollable: find.findSettingsScrollable(),
|
||||
);
|
||||
await tester.tapButton(find.byType(SignInOutButton));
|
||||
|
||||
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));
|
||||
await tester.pumpAndSettle(const Duration(seconds: 5));
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
@@ -56,7 +63,16 @@ void main() {
|
||||
|
||||
// should not see the sync setting page when sign in as anonymous
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
|
||||
// Scroll to sign-in
|
||||
await tester.scrollUntilVisible(
|
||||
find.byType(SignInOutButton),
|
||||
100,
|
||||
scrollable: find.findSettingsScrollable(),
|
||||
);
|
||||
await tester.tapButton(find.byType(SignInOutButton));
|
||||
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'anon_user_continue_test.dart' as anon_user_continue_test;
|
||||
import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test;
|
||||
import 'empty_test.dart' as preset_af_cloud_env_test;
|
||||
// import 'document_sync_test.dart' as document_sync_test;
|
||||
import 'user_setting_sync_test.dart' as user_sync_test;
|
||||
import 'workspace/change_name_and_icon_test.dart'
|
||||
as change_workspace_name_and_icon_test;
|
||||
@@ -10,13 +9,8 @@ import 'workspace/collaborative_workspace_test.dart'
|
||||
|
||||
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();
|
||||
|
||||
// workspace
|
||||
|
||||
@@ -8,14 +8,15 @@ 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/pages/settings_account_view.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 'package:path/path.dart' as p;
|
||||
|
||||
import '../shared/dir.dart';
|
||||
import '../shared/mock/mock_file_picker.dart';
|
||||
import '../shared/util.dart';
|
||||
@@ -45,12 +46,11 @@ void main() {
|
||||
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.openSettingsPage(SettingsPage.account);
|
||||
await tester.logout();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
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/pages/settings_account_view.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';
|
||||
|
||||
@@ -25,11 +23,8 @@ void main() {
|
||||
|
||||
// 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());
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
await tester.logout();
|
||||
|
||||
// Go to the sign in page again
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
@@ -42,7 +37,16 @@ void main() {
|
||||
|
||||
// should not see the sync setting page when sign in as anonymous
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
|
||||
// Scroll to sign-out
|
||||
await tester.scrollUntilVisible(
|
||||
find.byType(SignInOutButton),
|
||||
100,
|
||||
scrollable: find.findSettingsScrollable(),
|
||||
);
|
||||
await tester.tapButton(find.byType(SignInOutButton));
|
||||
|
||||
tester.expectToSeeGoogleLoginButton();
|
||||
});
|
||||
|
||||
|
||||
@@ -2,22 +2,27 @@
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.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/pages/settings_account_view.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:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../desktop/board/board_hide_groups_test.dart';
|
||||
import '../shared/database_test_op.dart';
|
||||
import '../shared/dir.dart';
|
||||
import '../shared/emoji.dart';
|
||||
@@ -39,62 +44,34 @@ void main() {
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
// final userAvatarFinder = find.descendant(
|
||||
// of: find.byType(SettingsUserView),
|
||||
// matching: find.byType(UserAvatar),
|
||||
// );
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
|
||||
// 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.enterUserName(name);
|
||||
await tester.tapEscButton();
|
||||
|
||||
// wait 2 seconds for the sync to finish
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
await tester.pumpAndSettle(const Duration(seconds: 6));
|
||||
});
|
||||
|
||||
|
||||
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.account);
|
||||
|
||||
// Verify name
|
||||
final profileSetting =
|
||||
tester.widget(find.byType(UserProfileSetting)) as UserProfileSetting;
|
||||
|
||||
expect(profileSetting.name, name);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.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';
|
||||
|
||||
@@ -10,11 +10,10 @@ 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/home/menu/sidebar/sidebar_workspace.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.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';
|
||||
@@ -33,66 +32,69 @@ import '../../shared/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final email = '${uuid()}@appflowy.io';
|
||||
// final email = '${uuid()}@appflowy.io';
|
||||
|
||||
group('collaborative workspace', () {
|
||||
// combine the create and delete workspace test to reduce the time
|
||||
testWidgets('create a new workspace, open it and then delete it',
|
||||
(tester) async {
|
||||
// only run the test when the feature flag is on
|
||||
if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
||||
return;
|
||||
}
|
||||
// if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
email: email,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
// await tester.initializeAppFlowy(
|
||||
// cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
// email: email,
|
||||
// );
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
// await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
const name = 'AppFlowy.IO';
|
||||
// the workspace will be opened after created
|
||||
await tester.createCollaborativeWorkspace(name);
|
||||
// const name = 'AppFlowy.IO';
|
||||
// // the workspace will be opened after created
|
||||
// await tester.createCollaborativeWorkspace(name);
|
||||
|
||||
final loading = find.byType(Loading);
|
||||
await tester.pumpUntilNotFound(loading);
|
||||
// final loading = find.byType(Loading);
|
||||
// await tester.pumpUntilNotFound(loading);
|
||||
|
||||
Finder success;
|
||||
// Finder success;
|
||||
|
||||
// delete the newly created workspace
|
||||
await tester.openCollaborativeWorkspaceMenu();
|
||||
final Finder items = find.byType(WorkspaceMenuItem);
|
||||
expect(items, findsNWidgets(2));
|
||||
expect(
|
||||
tester.widget<WorkspaceMenuItem>(items.last).workspace.name,
|
||||
name,
|
||||
);
|
||||
// final Finder items = find.byType(WorkspaceMenuItem);
|
||||
|
||||
final secondWorkspace = find.byType(WorkspaceMenuItem).last;
|
||||
await tester.hoverOnWidget(
|
||||
secondWorkspace,
|
||||
onHover: () async {
|
||||
// click the more button
|
||||
final moreButton = find.byType(WorkspaceMoreActionList);
|
||||
expect(moreButton, findsOneWidget);
|
||||
await tester.tapButton(moreButton);
|
||||
// click the delete button
|
||||
final deleteButton = find.text(LocaleKeys.button_delete.tr());
|
||||
expect(deleteButton, findsOneWidget);
|
||||
await tester.tapButton(deleteButton);
|
||||
// see the delete confirm dialog
|
||||
final confirm =
|
||||
find.text(LocaleKeys.workspace_deleteWorkspaceHintText.tr());
|
||||
expect(confirm, findsOneWidget);
|
||||
await tester.tapButton(find.text(LocaleKeys.button_ok.tr()));
|
||||
// delete success
|
||||
success = find.text(LocaleKeys.workspace_createSuccess.tr());
|
||||
await tester.pumpUntilFound(success);
|
||||
expect(success, findsOneWidget);
|
||||
await tester.pumpUntilNotFound(success);
|
||||
},
|
||||
);
|
||||
// // delete the newly created workspace
|
||||
// await tester.openCollaborativeWorkspaceMenu();
|
||||
// await tester.pumpUntilFound(items);
|
||||
|
||||
// expect(items, findsNWidgets(2));
|
||||
// expect(
|
||||
// tester.widget<WorkspaceMenuItem>(items.last).workspace.name,
|
||||
// name,
|
||||
// );
|
||||
|
||||
// final secondWorkspace = find.byType(WorkspaceMenuItem).last;
|
||||
// await tester.hoverOnWidget(
|
||||
// secondWorkspace,
|
||||
// onHover: () async {
|
||||
// // click the more button
|
||||
// final moreButton = find.byType(WorkspaceMoreActionList);
|
||||
// expect(moreButton, findsOneWidget);
|
||||
// await tester.tapButton(moreButton);
|
||||
// // click the delete button
|
||||
// final deleteButton = find.text(LocaleKeys.button_delete.tr());
|
||||
// expect(deleteButton, findsOneWidget);
|
||||
// await tester.tapButton(deleteButton);
|
||||
// // see the delete confirm dialog
|
||||
// final confirm =
|
||||
// find.text(LocaleKeys.workspace_deleteWorkspaceHintText.tr());
|
||||
// expect(confirm, findsOneWidget);
|
||||
// await tester.tapButton(find.text(LocaleKeys.button_ok.tr()));
|
||||
// // delete success
|
||||
// success = find.text(LocaleKeys.workspace_createSuccess.tr());
|
||||
// await tester.pumpUntilFound(success);
|
||||
// expect(success, findsOneWidget);
|
||||
// await tester.pumpUntilNotFound(success);
|
||||
// },
|
||||
// );
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.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/widgets/card/container/card_container.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:flutter/material.dart';
|
||||
@@ -22,13 +23,15 @@ void main() {
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findFirstCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(Text),
|
||||
);
|
||||
final firstCard = find.byType(RowCard).first;
|
||||
|
||||
Text firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.data, defaultFirstCardName);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: firstCard,
|
||||
matching: find.text(defaultFirstCardName),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
@@ -45,7 +48,7 @@ void main() {
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(RowCardContainer),
|
||||
of: firstCard,
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
@@ -55,8 +58,13 @@ void main() {
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
firstCardText = tester.firstWidget(findFirstCard);
|
||||
expect(firstCardText.data, newCardName);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(RowCard).first,
|
||||
matching: find.text(newCardName),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('from footer', (tester) async {
|
||||
@@ -65,39 +73,41 @@ void main() {
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final findLastCard = find.descendant(
|
||||
of: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(Text),
|
||||
final lastCard = find.byType(RowCard).last;
|
||||
|
||||
expect(
|
||||
find.descendant(
|
||||
of: lastCard,
|
||||
matching: find.text(defaultLastCardName),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
Text? lastCardText = tester.widgetList(findLastCard).last as Text;
|
||||
expect(lastCardText.data, defaultLastCardName);
|
||||
|
||||
await tester.tap(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(AppFlowyGroupFooter),
|
||||
matching: find.byType(FlowySvg),
|
||||
)
|
||||
.at(1),
|
||||
await tester.tapButton(
|
||||
find.byType(BoardColumnFooter).at(1),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const newCardName = 'Card 4';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(RowCardContainer),
|
||||
of: find.byType(BoardColumnFooter),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
newCardName,
|
||||
);
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||
|
||||
await tester.tap(find.byType(AppFlowyBoard));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
lastCardText = tester.widgetList(findLastCard).last as Text;
|
||||
expect(lastCardText.data, newCardName);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(RowCard).last,
|
||||
matching: find.text(newCardName),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.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 '../../shared/database_test_op.dart';
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board field test', () {
|
||||
testWidgets('change field type whithin card #5360', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
const name = 'Card 1';
|
||||
final card1 = find.text(name);
|
||||
await tester.tapButton(card1);
|
||||
|
||||
const fieldName = "test change field";
|
||||
await tester.createField(
|
||||
FieldType.RichText,
|
||||
fieldName,
|
||||
layout: ViewLayoutPB.Board,
|
||||
);
|
||||
await tester.tapButton(card1);
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
fieldName,
|
||||
FieldType.Checkbox,
|
||||
layout: ViewLayoutPB.Board,
|
||||
);
|
||||
await tester.hoverOnWidget(find.text('Card 2'));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:appflowy/plugins/database/widgets/card/card.dart';
|
||||
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 '../../shared/util.dart';
|
||||
|
||||
@@ -20,7 +20,7 @@ void main() {
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
final card1 = find.ancestor(
|
||||
of: find.text(card1Name),
|
||||
matching: find.byType(AppFlowyGroupCard),
|
||||
matching: find.byType(RowCard),
|
||||
);
|
||||
final doingGroup = find.text('Doing');
|
||||
final doingGroupCenter = tester.getCenter(doingGroup);
|
||||
|
||||
@@ -29,6 +29,7 @@ void main() {
|
||||
},
|
||||
);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
await tester.tapOKButton();
|
||||
expect(find.text(name), findsNothing);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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;
|
||||
import 'board_row_test.dart' as board_row_test;
|
||||
|
||||
void startTesting() {
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Board integration tests
|
||||
|
||||
@@ -41,15 +41,16 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
await tester.hoverOnFirstRowOfGrid(() async {
|
||||
// Open the row menu and then click the delete
|
||||
await tester.tapRowMenuButtonInGrid();
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tapDeleteOnRowMenu();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Open the row menu and then click the delete
|
||||
await tester.tapRowMenuButtonInGrid();
|
||||
await tester.tapDeleteOnRowMenu();
|
||||
|
||||
// 3 initial rows - 1 deleted
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
await tester.pumpAndSettle();
|
||||
// 3 initial rows - 1 deleted
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('check number of row indicator in the initial grid',
|
||||
|
||||
@@ -66,6 +66,7 @@ void main() {
|
||||
LogicalKeyboardKey.keyR,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], rightAlignmentKey);
|
||||
|
||||
@@ -77,6 +78,7 @@ void main() {
|
||||
LogicalKeyboardKey.keyE,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], centerAlignmentKey);
|
||||
|
||||
@@ -88,6 +90,7 @@ void main() {
|
||||
LogicalKeyboardKey.keyL,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
expect(first.attributes[blockComponentAlign], leftAlignmentKey);
|
||||
});
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
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';
|
||||
|
||||
@@ -106,61 +107,63 @@ void main() {
|
||||
expect(node2.type, ParagraphBlockKeys.type);
|
||||
expect(node3.type, ParagraphBlockKeys.type);
|
||||
expect(node1.delta!.toJson(), [
|
||||
{
|
||||
"insert": "void",
|
||||
"attributes": {"font_color": "0xfffede5d"},
|
||||
},
|
||||
{
|
||||
"insert": " ",
|
||||
"attributes": {"font_color": "0xffff7edb"},
|
||||
},
|
||||
{
|
||||
"insert": "main",
|
||||
"attributes": {"font_color": "0xff36f9f6"},
|
||||
},
|
||||
{
|
||||
"insert": "() {",
|
||||
"attributes": {"font_color": "0xffff7edb"},
|
||||
}
|
||||
{'insert': 'void main() {'},
|
||||
]);
|
||||
expect(node2.delta!.toJson(), [
|
||||
{
|
||||
"insert": " ",
|
||||
"attributes": {"font_color": "0xffff7edb"},
|
||||
},
|
||||
{
|
||||
"insert": "runApp",
|
||||
"attributes": {"font_color": "0xff36f9f6"},
|
||||
},
|
||||
{
|
||||
"insert": "(",
|
||||
"attributes": {"font_color": "0xffff7edb"},
|
||||
},
|
||||
{
|
||||
"insert": "const",
|
||||
"attributes": {"font_color": "0xfffede5d"},
|
||||
},
|
||||
{
|
||||
"insert": " ",
|
||||
"attributes": {"font_color": "0xffff7edb"},
|
||||
},
|
||||
{
|
||||
"insert": "MyApp",
|
||||
"attributes": {"font_color": "0xfffe4450"},
|
||||
},
|
||||
{
|
||||
"insert": "());",
|
||||
"attributes": {"font_color": "0xffff7edb"},
|
||||
}
|
||||
{'insert': " runApp(const MyApp());"},
|
||||
]);
|
||||
expect(node3.delta!.toJson(), [
|
||||
{
|
||||
"insert": "}",
|
||||
"attributes": {"font_color": "0xffff7edb"},
|
||||
}
|
||||
{"insert": "}"},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('paste bulleted list in numbered list', (tester) async {
|
||||
const inAppJson =
|
||||
'{"document":{"type":"page","children":[{"type":"bulleted_list","children":[{"type":"bulleted_list","data":{"delta":[{"insert":"World"}]}}],"data":{"delta":[{"insert":"Hello"}]}}]}}';
|
||||
|
||||
await tester.pasteContent(
|
||||
inAppJson: inAppJson,
|
||||
beforeTest: (editorState) async {
|
||||
final transaction = editorState.transaction;
|
||||
// Insert two numbered list nodes
|
||||
// 1. Parent One
|
||||
// 2.
|
||||
transaction.insertNodes(
|
||||
[0],
|
||||
[
|
||||
Node(
|
||||
type: NumberedListBlockKeys.type,
|
||||
attributes: {
|
||||
'delta': [
|
||||
{"insert": "One"},
|
||||
],
|
||||
},
|
||||
),
|
||||
Node(
|
||||
type: NumberedListBlockKeys.type,
|
||||
attributes: {'delta': []},
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Set the selection to the second numbered list node (which has empty delta)
|
||||
transaction.afterSelection = Selection.collapsed(Position(path: [1]));
|
||||
|
||||
await editorState.apply(transaction);
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
(editorState) {
|
||||
final secondNode = editorState.getNodeAtPath([1]);
|
||||
expect(secondNode?.delta?.toPlainText(), 'Hello');
|
||||
expect(secondNode?.children.length, 1);
|
||||
|
||||
final childNode = secondNode?.children.first;
|
||||
expect(childNode?.delta?.toPlainText(), 'World');
|
||||
expect(childNode?.type, BulletedListBlockKeys.type);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('paste image(png) from memory', (tester) async {
|
||||
@@ -255,42 +258,33 @@ void main() {
|
||||
testWidgets('paste the html content contains section', (tester) async {
|
||||
const html =
|
||||
'''<meta charset='utf-8'><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;">AppFlowy</strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;">Hello World</strong></span></section>''';
|
||||
await tester.pasteContent(
|
||||
html: html,
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node1 = editorState.getNodeAtPath([0])!;
|
||||
final node2 = editorState.getNodeAtPath([1])!;
|
||||
expect(node1.type, ParagraphBlockKeys.type);
|
||||
expect(node2.type, ParagraphBlockKeys.type);
|
||||
},
|
||||
);
|
||||
await tester.pasteContent(html: html, (editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node1 = editorState.getNodeAtPath([0])!;
|
||||
final node2 = editorState.getNodeAtPath([1])!;
|
||||
expect(node1.type, ParagraphBlockKeys.type);
|
||||
expect(node2.type, ParagraphBlockKeys.type);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('paste the html from google translation', (tester) async {
|
||||
const html =
|
||||
'''<meta charset='utf-8'><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;">new force</font></font></strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><span style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgb(0, 160, 113);"><strong style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; vertical-align: inherit;">Assessment focus: potential motivations, empathy</font></font></strong></span></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><br style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;"></section><section style="margin: 0px 8px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box; overflow-wrap: break-word !important; color: rgba(255, 255, 255, 0.6); font-family: system-ui, -apple-system, "system-ui", "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.544px; orphans: 2; text-align: justify; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(25, 25, 25); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><font style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-Line truncated
|
||||
await tester.pasteContent(
|
||||
html: html,
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 8);
|
||||
},
|
||||
);
|
||||
await tester.pasteContent(html: html, (editorState) {
|
||||
expect(editorState.document.root.children.length, 8);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'auto convert url to link preview block',
|
||||
(widgetTester) async {
|
||||
(tester) 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);
|
||||
},
|
||||
);
|
||||
await tester.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);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -301,6 +295,7 @@ extension on WidgetTester {
|
||||
Future<void> Function(EditorState editorState)? beforeTest,
|
||||
String? plainText,
|
||||
String? html,
|
||||
String? inAppJson,
|
||||
(String, Uint8List?)? image,
|
||||
}) async {
|
||||
await initializeAppFlowy();
|
||||
@@ -316,6 +311,7 @@ extension on WidgetTester {
|
||||
ClipboardServiceData(
|
||||
plainText: plainText,
|
||||
html: html,
|
||||
inAppJson: inAppJson,
|
||||
image: image,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -111,6 +111,7 @@ Future<void> triggerReferenceDocumentBySlashMenu(WidgetTester tester) async {
|
||||
LogicalKeyboardKey.enter,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
@@ -129,6 +130,7 @@ Future<void> enterDocumentText(WidgetTester tester) async {
|
||||
LogicalKeyboardKey.keyT,
|
||||
],
|
||||
tester: tester,
|
||||
withKeyUp: true,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('MoreViewActions', () {
|
||||
testWidgets('can duplicate and delete from menu', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final pageFinder = find.byType(ViewItem);
|
||||
expect(pageFinder, findsNWidgets(1));
|
||||
|
||||
// Duplicate
|
||||
await tester.openMoreViewActions();
|
||||
await tester.duplicateByMoreViewActions();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(pageFinder, findsNWidgets(2));
|
||||
|
||||
// Delete
|
||||
await tester.openMoreViewActions();
|
||||
await tester.deleteByMoreViewActions();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(pageFinder, findsNWidgets(1));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -6,6 +6,9 @@ import 'document_copy_and_paste_test.dart' as document_copy_and_paste_test;
|
||||
import 'document_create_and_delete_test.dart'
|
||||
as document_create_and_delete_test;
|
||||
import 'document_option_action_test.dart' as document_option_action_test;
|
||||
import 'document_inline_page_reference_test.dart'
|
||||
as document_inline_page_reference_test;
|
||||
import 'document_more_actions_test.dart' as document_more_actions_test;
|
||||
import 'document_text_direction_test.dart' as document_text_direction_test;
|
||||
import 'document_with_cover_image_test.dart' as document_with_cover_image_test;
|
||||
import 'document_with_database_test.dart' as document_with_database_test;
|
||||
@@ -13,11 +16,11 @@ import 'document_with_image_block_test.dart' as document_with_image_block_test;
|
||||
import 'document_with_inline_math_equation_test.dart'
|
||||
as document_with_inline_math_equation_test;
|
||||
import 'document_with_inline_page_test.dart' as document_with_inline_page_test;
|
||||
import 'document_with_multi_image_block_test.dart'
|
||||
as document_with_multi_image_block_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();
|
||||
@@ -37,5 +40,7 @@ void startTesting() {
|
||||
document_text_direction_test.main();
|
||||
document_option_action_test.main();
|
||||
document_with_image_block_test.main();
|
||||
document_with_multi_image_block_test.main();
|
||||
document_inline_page_reference_test.main();
|
||||
document_more_actions_test.main();
|
||||
}
|
||||
@@ -130,24 +130,24 @@ void main() {
|
||||
final searchEmojiTextField = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is TextField &&
|
||||
widget.decoration!.hintText == LocaleKeys.emoji_search.tr(),
|
||||
widget.decoration!.hintText == LocaleKeys.search_label.tr(),
|
||||
);
|
||||
await tester.enterText(
|
||||
searchEmojiTextField,
|
||||
'hand',
|
||||
'punch',
|
||||
);
|
||||
|
||||
// change skin tone
|
||||
await tester.editor.changeEmojiSkinTone(EmojiSkinTone.dark);
|
||||
|
||||
// select an icon with skin tone
|
||||
const hand = '👋🏿';
|
||||
await tester.tapEmoji(hand);
|
||||
tester.expectToSeeDocumentIcon(hand);
|
||||
const punch = '👊🏿';
|
||||
await tester.tapEmoji(punch);
|
||||
tester.expectToSeeDocumentIcon(punch);
|
||||
tester.expectViewHasIcon(
|
||||
gettingStarted,
|
||||
ViewLayoutPB.Document,
|
||||
hand,
|
||||
punch,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ void main() {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyEditor),
|
||||
matching: find.byType(BoardPage),
|
||||
matching: find.byType(DesktopBoardPage),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
@@ -104,7 +104,7 @@ void main() {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyEditor),
|
||||
matching: find.byType(BoardPage),
|
||||
matching: find.byType(DesktopBoardPage),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
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/custom_image_block_component/custom_image_block_component.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/plugins/document/presentation/editor_plugins/image/upload_image_menu/upload_image_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
|
||||
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';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@@ -36,7 +37,7 @@ void main() {
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -84,7 +85,7 @@ void main() {
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -137,7 +138,7 @@ void main() {
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImage.tr(),
|
||||
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
@@ -161,5 +162,67 @@ void main() {
|
||||
expect(find.byType(UnsplashImageWidget), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('insert two images from local file at once', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: LocaleKeys.document_plugins_image_addAnImageDesktop.tr(),
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Image');
|
||||
expect(find.byType(CustomImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(ImagePlaceholder), findsOneWidget);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(ImagePlaceholder),
|
||||
matching: find.byType(AppFlowyPopover),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
final firstImage =
|
||||
await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final secondImage =
|
||||
await rootBundle.load('assets/test/images/sample.gif');
|
||||
final tempDirectory = await getTemporaryDirectory();
|
||||
|
||||
final firstImagePath = p.join(tempDirectory.path, 'sample.jpeg');
|
||||
final firstFile = File(firstImagePath)
|
||||
..writeAsBytesSync(firstImage.buffer.asUint8List());
|
||||
|
||||
final secondImagePath = p.join(tempDirectory.path, 'sample.gif');
|
||||
final secondFile = File(secondImagePath)
|
||||
..writeAsBytesSync(secondImage.buffer.asUint8List());
|
||||
|
||||
mockPickFilePaths(paths: [firstImagePath, secondImagePath]);
|
||||
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(ResizableImage), findsNWidgets(2));
|
||||
|
||||
final firstNode =
|
||||
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(firstNode.type, ImageBlockKeys.type);
|
||||
expect(firstNode.attributes[ImageBlockKeys.url], isNotEmpty);
|
||||
|
||||
final secondNode =
|
||||
tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(secondNode.type, ImageBlockKeys.type);
|
||||
expect(secondNode.attributes[ImageBlockKeys.url], isNotEmpty);
|
||||
|
||||
// remove the temp files
|
||||
await Future.wait([firstFile.delete(), secondFile.delete()]);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
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/image/multi_image_block_component/multi_image_block_component.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_block_component/multi_image_placeholder.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/multi_image_layouts.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/upload_image_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/shared/appflowy_network_image.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_toolbar.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/image_viewer/interactive_image_viewer.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../../shared/mock/mock_file_picker.dart';
|
||||
import '../../shared/util.dart';
|
||||
import '../board/board_hide_groups_test.dart';
|
||||
|
||||
void main() {
|
||||
setUp(() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
});
|
||||
|
||||
group('multi image block in document', () {
|
||||
testWidgets('insert images from local and use interactive viewer',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'multi image block test',
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Photo gallery');
|
||||
expect(find.byType(MultiImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(MultiImagePlaceholder), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byType(MultiImagePlaceholder));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
final firstImage =
|
||||
await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final secondImage =
|
||||
await rootBundle.load('assets/test/images/sample.gif');
|
||||
final tempDirectory = await getTemporaryDirectory();
|
||||
|
||||
final firstImagePath = p.join(tempDirectory.path, 'sample.jpeg');
|
||||
final firstFile = File(firstImagePath)
|
||||
..writeAsBytesSync(firstImage.buffer.asUint8List());
|
||||
|
||||
final secondImagePath = p.join(tempDirectory.path, 'sample.gif');
|
||||
final secondFile = File(secondImagePath)
|
||||
..writeAsBytesSync(secondImage.buffer.asUint8List());
|
||||
|
||||
mockPickFilePaths(paths: [firstImagePath, secondImagePath]);
|
||||
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(ImageBrowserLayout), findsOneWidget);
|
||||
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(node.type, MultiImageBlockKeys.type);
|
||||
|
||||
final data = MultiImageData.fromJson(
|
||||
node.attributes[MultiImageBlockKeys.images],
|
||||
);
|
||||
|
||||
expect(data.images.length, 2);
|
||||
|
||||
// Start using the interactive viewer to view the image(s)
|
||||
final imageFinder = find
|
||||
.byWidgetPredicate(
|
||||
(w) =>
|
||||
w is Image &&
|
||||
w.image is FileImage &&
|
||||
(w.image as FileImage).file.path.endsWith('.jpeg'),
|
||||
)
|
||||
.first;
|
||||
await tester.tap(imageFinder);
|
||||
await tester.pump(kDoubleTapMinTime);
|
||||
await tester.tap(imageFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final ivFinder = find.byType(InteractiveImageViewer);
|
||||
expect(ivFinder, findsOneWidget);
|
||||
|
||||
// go to next image
|
||||
await tester.tap(find.byFlowySvg(FlowySvgs.arrow_right_s));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect image to end with .gif
|
||||
final gifImageFinder = find.byWidgetPredicate(
|
||||
(w) =>
|
||||
w is Image &&
|
||||
w.image is FileImage &&
|
||||
(w.image as FileImage).file.path.endsWith('.gif'),
|
||||
);
|
||||
|
||||
gifImageFinder.evaluate();
|
||||
expect(gifImageFinder.found.length, 2);
|
||||
|
||||
// go to previous image
|
||||
await tester.tap(find.byFlowySvg(FlowySvgs.arrow_left_s));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
gifImageFinder.evaluate();
|
||||
expect(gifImageFinder.found.length, 1);
|
||||
|
||||
// remove the temp files
|
||||
await Future.wait([firstFile.delete(), secondFile.delete()]);
|
||||
});
|
||||
|
||||
testWidgets('insert and delete images from network', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'multi image block test',
|
||||
);
|
||||
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName('Photo gallery');
|
||||
expect(find.byType(MultiImageBlockComponent), findsOneWidget);
|
||||
expect(find.byType(MultiImagePlaceholder), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byType(MultiImagePlaceholder));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
);
|
||||
|
||||
const url =
|
||||
'https://images.unsplash.com/photo-1469474968028-56623f02e42e?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=david-marcu-78A265wPiO4-unsplash.jpg&w=640';
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
url,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.text(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
findRichText: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(ImageBrowserLayout), findsOneWidget);
|
||||
final node = tester.editor.getCurrentEditorState().getNodeAtPath([0])!;
|
||||
expect(node.type, MultiImageBlockKeys.type);
|
||||
|
||||
final data = MultiImageData.fromJson(
|
||||
node.attributes[MultiImageBlockKeys.images],
|
||||
);
|
||||
|
||||
expect(data.images.length, 1);
|
||||
|
||||
final imageFinder = find
|
||||
.byWidgetPredicate(
|
||||
(w) => w is FlowyNetworkImage && w.url == url,
|
||||
)
|
||||
.first;
|
||||
|
||||
// Insert two images from network
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// Hover on the image to show the image toolbar
|
||||
await tester.hoverOnWidget(
|
||||
imageFinder,
|
||||
onHover: () async {
|
||||
// Click on the add
|
||||
final addFinder = find.descendant(
|
||||
of: find.byType(MultiImageMenu),
|
||||
matching: find.byFlowySvg(FlowySvgs.add_s),
|
||||
);
|
||||
|
||||
expect(addFinder, findsOneWidget);
|
||||
await tester.tap(addFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
);
|
||||
|
||||
await tester.enterText(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.byType(TextField),
|
||||
),
|
||||
url,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(EmbedImageUrlWidget),
|
||||
matching: find.text(
|
||||
LocaleKeys.document_imageBlock_embedLink_label.tr(),
|
||||
findRichText: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// There should be 4 images visible now, where 2 are thumbnails
|
||||
expect(find.byType(ThumbnailItem), findsNWidgets(3));
|
||||
|
||||
// And all three use ImageRender
|
||||
expect(find.byType(ImageRender), findsNWidgets(4));
|
||||
|
||||
// Hover on and delete the first thumbnail image
|
||||
await tester.hoverOnWidget(find.byType(ThumbnailItem).first);
|
||||
|
||||
final deleteFinder = find
|
||||
.descendant(
|
||||
of: find.byType(ThumbnailItem),
|
||||
matching: find.byFlowySvg(FlowySvgs.delete_s),
|
||||
)
|
||||
.first;
|
||||
|
||||
expect(deleteFinder, findsOneWidget);
|
||||
await tester.tap(deleteFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(ImageRender), findsNWidgets(3));
|
||||
|
||||
// Delete one from interactive viewer
|
||||
await tester.tap(imageFinder);
|
||||
await tester.pump(kDoubleTapMinTime);
|
||||
await tester.tap(imageFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final ivFinder = find.byType(InteractiveImageViewer);
|
||||
expect(ivFinder, findsOneWidget);
|
||||
|
||||
await tester.tap(
|
||||
find.descendant(
|
||||
of: find.byType(InteractiveImageToolbar),
|
||||
matching: find.byFlowySvg(FlowySvgs.delete_s),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(InteractiveImageViewer), findsNothing);
|
||||
|
||||
// There should be 1 image and the thumbnail for said image still visible
|
||||
expect(find.byType(ImageRender), findsNWidgets(2));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -8,8 +8,8 @@ import '../../shared/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('board add row test', () {
|
||||
testWidgets('Add card from header', (tester) async {
|
||||
group('notification test', () {
|
||||
testWidgets('enable notification', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
@@ -17,25 +17,25 @@ void main() {
|
||||
await tester.openSettingsPage(SettingsPage.notifications);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final switchFinder = find.byType(Switch);
|
||||
final toggleFinder = find.byType(Toggle).first;
|
||||
|
||||
// Defaults to enabled
|
||||
Switch switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, true);
|
||||
Toggle toggleWidget = tester.widget(toggleFinder);
|
||||
expect(toggleWidget.value, true);
|
||||
|
||||
// Disable
|
||||
await tester.tap(switchFinder);
|
||||
await tester.tap(toggleFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, false);
|
||||
toggleWidget = tester.widget(toggleFinder);
|
||||
expect(toggleWidget.value, false);
|
||||
|
||||
// Enable again
|
||||
await tester.tap(switchFinder);
|
||||
await tester.tap(toggleFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switchWidget = tester.widget(switchFinder);
|
||||
expect(switchWidget.value, true);
|
||||
toggleWidget = tester.widget(toggleFinder);
|
||||
expect(toggleWidget.value, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/auth_operation.dart';
|
||||
import '../../shared/base.dart';
|
||||
import '../../shared/expectation.dart';
|
||||
import '../../shared/settings.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Settings Billing', () {
|
||||
testWidgets('Local auth cannot see plan+billing', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapSignInAsGuest();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// We check that another settings page is present to ensure
|
||||
// it's not a fluke
|
||||
expect(
|
||||
find.text(
|
||||
LocaleKeys.settings_workspacePage_menuLabel.tr(),
|
||||
skipOffstage: false,
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
expect(
|
||||
find.text(
|
||||
LocaleKeys.settings_planPage_menuLabel.tr(),
|
||||
skipOffstage: false,
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
|
||||
expect(
|
||||
find.text(
|
||||
LocaleKeys.settings_billingPage_menuLabel.tr(),
|
||||
skipOffstage: false,
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
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;
|
||||
import 'settings_billing_test.dart' as settings_billing_test;
|
||||
import 'shortcuts_settings_test.dart' as shortcuts_settings_test;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
notifications_settings_test.main();
|
||||
user_language_test.main();
|
||||
settings_billing_test.main();
|
||||
shortcuts_settings_test.main();
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
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/workspace/application/settings/settings_dialog_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_shortcuts_view.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/keyboard.dart';
|
||||
import '../../shared/util.dart';
|
||||
import '../board/board_hide_groups_test.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('shortcuts test', () {
|
||||
testWidgets('can change and overwrite shortcut', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.shortcuts);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final backspaceCmd =
|
||||
LocaleKeys.settings_shortcutsPage_keybindings_backspace.tr();
|
||||
|
||||
// Input "Delete" into the search field
|
||||
await tester.enterText(find.byType(TextField), backspaceCmd);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.hoverOnWidget(
|
||||
find.descendant(
|
||||
of: find.byType(ShortcutSettingTile),
|
||||
matching: find.text(backspaceCmd),
|
||||
),
|
||||
onHover: () async {
|
||||
await tester.tap(find.byFlowySvg(FlowySvgs.edit_s));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
[
|
||||
LogicalKeyboardKey.delete,
|
||||
LogicalKeyboardKey.enter,
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
);
|
||||
|
||||
// We expect to see conflict dialog
|
||||
expect(
|
||||
find.text(
|
||||
LocaleKeys.settings_shortcutsPage_conflictDialog_confirmLabel.tr(),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// Press on confirm label
|
||||
await tester.tap(
|
||||
find.text(
|
||||
LocaleKeys.settings_shortcutsPage_conflictDialog_confirmLabel.tr(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// We expect the first ShortcutSettingTile to have one
|
||||
// [KeyBadge] with `delete` label
|
||||
final first = tester.widget(find.byType(ShortcutSettingTile).first)
|
||||
as ShortcutSettingTile;
|
||||
expect(
|
||||
first.command.command,
|
||||
'delete',
|
||||
);
|
||||
|
||||
// And the second one which is `Delete left character` to have none
|
||||
// as it will have been overwritten
|
||||
final second = tester.widget(find.byType(ShortcutSettingTile).at(1))
|
||||
as ShortcutSettingTile;
|
||||
expect(
|
||||
second.command.command,
|
||||
'',
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Settings: user language tests', () {
|
||||
testWidgets('select language, language changed', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.language);
|
||||
|
||||
final userLanguageFinder = find.descendant(
|
||||
of: find.byType(SettingsLanguageView),
|
||||
matching: find.byType(LanguageSelector),
|
||||
);
|
||||
|
||||
// Grab current locale
|
||||
LanguageSelector userLanguage =
|
||||
tester.widget<LanguageSelector>(userLanguageFinder);
|
||||
Locale currentLocale = userLanguage.currentLocale;
|
||||
|
||||
// Open language selector
|
||||
await tester.tap(userLanguageFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select first option that isn't default
|
||||
await tester.tap(find.byType(LanguageItem).at(1));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Make sure the new locale is not the same as previous one
|
||||
userLanguage = tester.widget<LanguageSelector>(userLanguageFinder);
|
||||
expect(
|
||||
userLanguage.currentLocale,
|
||||
isNot(equals(currentLocale)),
|
||||
reason: "new language shouldn't equal the previous selected language",
|
||||
);
|
||||
|
||||
// Update the current locale to a new one
|
||||
currentLocale = userLanguage.currentLocale;
|
||||
|
||||
// Tried the same flow for the second time
|
||||
// Open language selector
|
||||
await tester.tap(userLanguageFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Select second option that isn't default
|
||||
await tester.tap(find.byType(LanguageItem).at(2));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Make sure the new locale is not the same as previous one
|
||||
userLanguage = tester.widget<LanguageSelector>(userLanguageFinder);
|
||||
expect(
|
||||
userLanguage.currentLocale,
|
||||
isNot(equals(currentLocale)),
|
||||
reason: "new language shouldn't equal the previous selected language",
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_folder.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_folder.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -12,8 +12,8 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('sidebar expand test', () {
|
||||
bool isExpanded({required FolderCategoryType type}) {
|
||||
if (type == FolderCategoryType.private) {
|
||||
bool isExpanded({required FolderSpaceType type}) {
|
||||
if (type == FolderSpaceType.private) {
|
||||
return find
|
||||
.descendant(
|
||||
of: find.byType(PrivateSectionFolder),
|
||||
@@ -30,19 +30,19 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// first time is expanded
|
||||
expect(isExpanded(type: FolderCategoryType.private), true);
|
||||
expect(isExpanded(type: FolderSpaceType.private), true);
|
||||
|
||||
// collapse the personal folder
|
||||
await tester.tapButton(
|
||||
find.byTooltip(LocaleKeys.sideBar_clickToHidePrivate.tr()),
|
||||
);
|
||||
expect(isExpanded(type: FolderCategoryType.private), false);
|
||||
expect(isExpanded(type: FolderSpaceType.private), false);
|
||||
|
||||
// expand the personal folder
|
||||
await tester.tapButton(
|
||||
find.byTooltip(LocaleKeys.sideBar_clickToHidePrivate.tr()),
|
||||
);
|
||||
expect(isExpanded(type: FolderCategoryType.private), true);
|
||||
expect(isExpanded(type: FolderSpaceType.private), true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
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/sidebar/favorites/favorite_folder.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
@@ -46,7 +46,7 @@ void main() {
|
||||
await tester.favoriteViewByName(names[1]);
|
||||
expect(
|
||||
tester.findFavoritePageName(names[1]),
|
||||
findsNWidgets(2),
|
||||
findsNWidgets(1),
|
||||
);
|
||||
|
||||
await tester.unfavoriteViewByName(gettingStarted);
|
||||
@@ -120,9 +120,9 @@ void main() {
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite,
|
||||
widget.spaceType == FolderSpaceType.favorite,
|
||||
),
|
||||
findsNWidgets(6),
|
||||
findsNWidgets(3),
|
||||
);
|
||||
|
||||
await tester.hoverOnPageName(
|
||||
@@ -135,7 +135,7 @@ void main() {
|
||||
|
||||
expect(
|
||||
tester.findAllFavoritePages(),
|
||||
findsNWidgets(3),
|
||||
findsNWidgets(2),
|
||||
);
|
||||
|
||||
await tester.hoverOnPageName(
|
||||
@@ -168,7 +168,7 @@ void main() {
|
||||
widget.isSelected != null &&
|
||||
widget.isSelected!(),
|
||||
),
|
||||
findsNWidgets(2),
|
||||
findsNWidgets(1),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -18,6 +18,9 @@ void main() {
|
||||
|
||||
// create document, board, grid and calendar views
|
||||
for (final value in ViewLayoutPB.values) {
|
||||
if (value == ViewLayoutPB.Chat) {
|
||||
continue;
|
||||
}
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
@@ -46,6 +49,9 @@ void main() {
|
||||
|
||||
// create document, board, grid and calendar views
|
||||
for (final value in ViewLayoutPB.values) {
|
||||
if (value == ViewLayoutPB.Chat) {
|
||||
continue;
|
||||
}
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: value.name,
|
||||
parentName: gettingStarted,
|
||||
|
||||
@@ -39,6 +39,9 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
for (final layout in ViewLayoutPB.values) {
|
||||
if (layout == ViewLayoutPB.Chat) {
|
||||
continue;
|
||||
}
|
||||
// create a new page
|
||||
final name = 'AppFlowy_$layout';
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
@@ -61,11 +64,13 @@ void main() {
|
||||
expect(find.byType(GridPage), findsOneWidget);
|
||||
break;
|
||||
case ViewLayoutPB.Board:
|
||||
expect(find.byType(BoardPage), findsOneWidget);
|
||||
expect(find.byType(DesktopBoardPage), findsOneWidget);
|
||||
break;
|
||||
case ViewLayoutPB.Calendar:
|
||||
expect(find.byType(CalendarPage), findsOneWidget);
|
||||
break;
|
||||
case ViewLayoutPB.Chat:
|
||||
break;
|
||||
}
|
||||
|
||||
await tester.openPage(gettingStarted);
|
||||
|
||||
@@ -4,7 +4,7 @@ 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() {
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Sidebar integration tests
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import 'package:appflowy/workspace/application/appearance_defaults.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/settings_appearance.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('appearance settings tests', () {
|
||||
testWidgets('after editing text field, button should be able to be clicked',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
|
||||
final dropDown = find.byKey(ThemeFontFamilySetting.popoverKey);
|
||||
await tester.tap(dropDown);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final textField = find.byKey(ThemeFontFamilySetting.textFieldKey);
|
||||
await tester.tap(textField);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(textField, 'Abel');
|
||||
await tester.pumpAndSettle();
|
||||
final fontFamilyButton = find.byKey(const Key('Abel'));
|
||||
|
||||
expect(fontFamilyButton, findsOneWidget);
|
||||
await tester.tap(fontFamilyButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// just switch the page and verify that the font family was set after that
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
|
||||
expect(find.textContaining('Abel'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('reset the font family', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
await tester.openSettings();
|
||||
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
|
||||
final dropDown = find.byKey(ThemeFontFamilySetting.popoverKey);
|
||||
await tester.tap(dropDown);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final textField = find.byKey(ThemeFontFamilySetting.textFieldKey);
|
||||
await tester.tap(textField);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(textField, 'Abel');
|
||||
await tester.pumpAndSettle();
|
||||
final fontFamilyButton = find.byKey(const Key('Abel'));
|
||||
|
||||
expect(fontFamilyButton, findsOneWidget);
|
||||
await tester.tap(fontFamilyButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// just switch the page and verify that the font family was set after that
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
|
||||
final resetButton = find.byKey(ThemeFontFamilySetting.resetButtonkey);
|
||||
await tester.tap(resetButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// just switch the page and verify that the font family was set after that
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
|
||||
expect(
|
||||
find.textContaining(DefaultAppearanceSettings.kDefaultFontFamily),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -8,9 +8,10 @@ void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Empty', () {
|
||||
testWidgets('toggle theme mode', (tester) async {
|
||||
testWidgets('empty test', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.wait(1000);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/settings_dialog.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';
|
||||
|
||||
@@ -23,31 +25,35 @@ void main() {
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.appearance);
|
||||
await tester.openSettingsPage(SettingsPage.workspace);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
tester.expectToSeeText(
|
||||
LocaleKeys.settings_appearance_themeMode_system.tr(),
|
||||
);
|
||||
final appFinder = find.byType(MaterialApp).first;
|
||||
ThemeMode? themeMode = tester.widget<MaterialApp>(appFinder).themeMode;
|
||||
|
||||
expect(themeMode, ThemeMode.system);
|
||||
|
||||
await tester.tapButton(
|
||||
find.bySemanticsLabel(
|
||||
LocaleKeys.settings_appearance_themeMode_system.tr(),
|
||||
LocaleKeys.settings_workspacePage_appearance_options_light.tr(),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
themeMode = tester.widget<MaterialApp>(appFinder).themeMode;
|
||||
expect(themeMode, ThemeMode.light);
|
||||
|
||||
await tester.tapButton(
|
||||
find.bySemanticsLabel(
|
||||
LocaleKeys.settings_appearance_themeMode_dark.tr(),
|
||||
LocaleKeys.settings_workspacePage_appearance_options_dark.tr(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
themeMode = tester.widget<MaterialApp>(appFinder).themeMode;
|
||||
expect(themeMode, ThemeMode.dark);
|
||||
|
||||
await tester.tap(find.byType(SettingsDialog));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await FlowyTestKeyboard.simulateKeyDownEvent(
|
||||
@@ -60,12 +66,10 @@ void main() {
|
||||
],
|
||||
tester: tester,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
tester.expectToSeeText(
|
||||
LocaleKeys.settings_appearance_themeMode_light.tr(),
|
||||
);
|
||||
themeMode = tester.widget<MaterialApp>(appFinder).themeMode;
|
||||
expect(themeMode, ThemeMode.light);
|
||||
});
|
||||
|
||||
testWidgets('show or hide home menu', (tester) async {
|
||||
|
||||
@@ -82,11 +82,11 @@ void main() {
|
||||
HeadingBlockKeys.type,
|
||||
);
|
||||
expect(
|
||||
importedPageEditorState.getNodeAtPath([2])!.type,
|
||||
importedPageEditorState.getNodeAtPath([1])!.type,
|
||||
HeadingBlockKeys.type,
|
||||
);
|
||||
expect(
|
||||
importedPageEditorState.getNodeAtPath([4])!.type,
|
||||
importedPageEditorState.getNodeAtPath([2])!.type,
|
||||
TableBlockKeys.type,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_language_view.dart';
|
||||
import 'package:appflowy/user/presentation/screens/skip_log_in_screen.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/ai_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';
|
||||
@@ -103,8 +103,8 @@ Future<AppFlowyEditor> setUpOpenAITesting(WidgetTester tester) async {
|
||||
}
|
||||
|
||||
Future<void> mockOpenAIRepository() async {
|
||||
await getIt.unregister<OpenAIRepository>();
|
||||
getIt.registerFactoryAsync<OpenAIRepository>(
|
||||
await getIt.unregister<AIRepository>();
|
||||
getIt.registerFactoryAsync<AIRepository>(
|
||||
() => Future.value(
|
||||
MockOpenAIRepository(),
|
||||
),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
|
||||
import 'package:appflowy/plugins/shared/share/share_button.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@@ -51,13 +51,13 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
final shareButton = find.byType(ShareActionList);
|
||||
final shareButtonState =
|
||||
tester.state(shareButton) as ShareActionListState;
|
||||
final shareButton = find.byType(ShareButton);
|
||||
final shareButtonState = tester.widget(shareButton) as ShareButton;
|
||||
|
||||
final path = await mockSaveFilePath(
|
||||
p.join(
|
||||
context.applicationDataDirectory,
|
||||
'${shareButtonState.name}.md',
|
||||
'${shareButtonState.view.name}.md',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ void main() {
|
||||
|
||||
// open settings and restore the location
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.files);
|
||||
await tester.openSettingsPage(SettingsPage.manageData);
|
||||
await tester.restoreLocation();
|
||||
|
||||
expect(
|
||||
|
||||
@@ -3,8 +3,6 @@ import 'package:integration_test/integration_test.dart';
|
||||
import 'desktop/board/board_test_runner.dart' as board_test_runner;
|
||||
import 'desktop/settings/settings_runner.dart' as settings_test_runner;
|
||||
import 'desktop/sidebar/sidebar_test_runner.dart' as sidebar_test_runner;
|
||||
import 'desktop/uncategorized/appearance_settings_test.dart'
|
||||
as appearance_test_runner;
|
||||
import 'desktop/uncategorized/emoji_shortcut_test.dart' as emoji_shortcut_test;
|
||||
import 'desktop/uncategorized/empty_test.dart' as first_test;
|
||||
import 'desktop/uncategorized/hotkeys_test.dart' as hotkeys_test;
|
||||
@@ -26,11 +24,10 @@ Future<void> runIntegration3OnDesktop() async {
|
||||
emoji_shortcut_test.main();
|
||||
hotkeys_test.main();
|
||||
emoji_shortcut_test.main();
|
||||
appearance_test_runner.main();
|
||||
settings_test_runner.main();
|
||||
share_markdown_test.main();
|
||||
import_files_test.main();
|
||||
sidebar_test_runner.startTesting();
|
||||
board_test_runner.startTesting();
|
||||
sidebar_test_runner.main();
|
||||
board_test_runner.main();
|
||||
tabs_test.main();
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.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';
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/application/page_style/document_page_style_bloc.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar_actions.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/view_page/app_bar_buttons.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet_buttons.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/home.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_page.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/document_immersive_cover.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/cover/document_immersive_cover_bloc.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/page_style/_page_style_layout.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/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_ext.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.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:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../shared/dir.dart';
|
||||
import '../../shared/mock/mock_file_picker.dart';
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('document page style', () {
|
||||
double getCurrentEditorFontSize() {
|
||||
final editorPage = find
|
||||
.byType(AppFlowyEditorPage)
|
||||
.evaluate()
|
||||
.single
|
||||
.widget as AppFlowyEditorPage;
|
||||
return editorPage.styleCustomizer
|
||||
.style()
|
||||
.textStyleConfiguration
|
||||
.text
|
||||
.fontSize!;
|
||||
}
|
||||
|
||||
double getCurrentEditorLineHeight() {
|
||||
final editorPage = find
|
||||
.byType(AppFlowyEditorPage)
|
||||
.evaluate()
|
||||
.single
|
||||
.widget as AppFlowyEditorPage;
|
||||
return editorPage.styleCustomizer
|
||||
.style()
|
||||
.textStyleConfiguration
|
||||
.text
|
||||
.height!;
|
||||
}
|
||||
|
||||
testWidgets('change font size in page style settings', (tester) async {
|
||||
await tester.launchInAnonymousMode();
|
||||
|
||||
// click the getting start page
|
||||
await tester.openPage(gettingStarted);
|
||||
// click the layout button
|
||||
await tester.tapButton(find.byType(MobileViewPageLayoutButton));
|
||||
expect(getCurrentEditorFontSize(), PageStyleFontLayout.normal.fontSize);
|
||||
// change font size from normal to large
|
||||
await tester.tapSvgButton(FlowySvgs.m_font_size_large_s);
|
||||
expect(getCurrentEditorFontSize(), PageStyleFontLayout.large.fontSize);
|
||||
// change font size from large to small
|
||||
await tester.tapSvgButton(FlowySvgs.m_font_size_small_s);
|
||||
expect(getCurrentEditorFontSize(), PageStyleFontLayout.small.fontSize);
|
||||
});
|
||||
|
||||
testWidgets('change line height in page style settings', (tester) async {
|
||||
await tester.launchInAnonymousMode();
|
||||
|
||||
// click the getting start page
|
||||
await tester.openPage(gettingStarted);
|
||||
// click the layout button
|
||||
await tester.tapButton(find.byType(MobileViewPageLayoutButton));
|
||||
expect(
|
||||
getCurrentEditorLineHeight(),
|
||||
PageStyleLineHeightLayout.normal.lineHeight,
|
||||
);
|
||||
// change line height from normal to large
|
||||
await tester.tapSvgButton(FlowySvgs.m_layout_large_s);
|
||||
expect(
|
||||
getCurrentEditorLineHeight(),
|
||||
PageStyleLineHeightLayout.large.lineHeight,
|
||||
);
|
||||
// change line height from large to small
|
||||
await tester.tapSvgButton(FlowySvgs.m_layout_small_s);
|
||||
expect(
|
||||
getCurrentEditorLineHeight(),
|
||||
PageStyleLineHeightLayout.small.lineHeight,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('use built-in image as cover', (tester) async {
|
||||
await tester.launchInAnonymousMode();
|
||||
|
||||
// click the getting start page
|
||||
await tester.openPage(gettingStarted);
|
||||
// click the layout button
|
||||
await tester.tapButton(find.byType(MobileViewPageLayoutButton));
|
||||
// toggle the preset button
|
||||
await tester.tapSvgButton(FlowySvgs.m_page_style_presets_m);
|
||||
|
||||
// select the first preset
|
||||
final firstBuiltInImage = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is Image &&
|
||||
widget.image is AssetImage &&
|
||||
(widget.image as AssetImage).assetName ==
|
||||
PageStyleCoverImageType.builtInImagePath('1'),
|
||||
);
|
||||
await tester.tap(firstBuiltInImage);
|
||||
|
||||
// click done button to exit the page style settings
|
||||
await tester.tapButton(find.byType(BottomSheetDoneButton).first);
|
||||
await tester.tapButton(find.byType(BottomSheetDoneButton).first);
|
||||
|
||||
// check the cover
|
||||
final builtInCover = find.descendant(
|
||||
of: find.byType(DocumentImmersiveCover),
|
||||
matching: firstBuiltInImage,
|
||||
);
|
||||
expect(builtInCover, findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.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';
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'mobile/home_page/create_new_page_test.dart' as create_new_page_test;
|
||||
import 'mobile/sign_in/anonymous_sign_in_test.dart' as anonymous_sign_in_test;
|
||||
|
||||
Future<void> runIntegrationOnMobile() async {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
anonymous_sign_in_test.main();
|
||||
create_new_page_test.main();
|
||||
}
|
||||
@@ -1,22 +1,32 @@
|
||||
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/pages/settings_account_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:appflowy/workspace/presentation/widgets/toggle/toggle.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';
|
||||
import 'util.dart';
|
||||
|
||||
extension AppFlowyAuthTest on WidgetTester {
|
||||
Future<void> tapGoogleLoginInButton() async {
|
||||
await tapButton(find.byKey(const Key('signInWithGoogleButton')));
|
||||
await tapButton(
|
||||
find.byKey(const Key('signInWithGoogleButton')),
|
||||
);
|
||||
}
|
||||
|
||||
/// Requires being on the SettingsPage.account of the SettingsDialog
|
||||
Future<void> logout() async {
|
||||
await tapButton(find.byType(SettingLogoutButton));
|
||||
final scrollable = find.findSettingsScrollable();
|
||||
await scrollUntilVisible(
|
||||
find.byType(SignInOutButton),
|
||||
100,
|
||||
scrollable: scrollable,
|
||||
);
|
||||
|
||||
await tapButton(find.byType(SignInOutButton));
|
||||
|
||||
expectToSeeText(LocaleKeys.button_ok.tr());
|
||||
await tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
@@ -36,6 +46,12 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
assert(isSwitched == value);
|
||||
}
|
||||
|
||||
void assertToggleValue(Finder finder, bool value) {
|
||||
final Toggle switchWidget = widget(finder);
|
||||
final isSwitched = switchWidget.value;
|
||||
assert(isSwitched == value);
|
||||
}
|
||||
|
||||
void assertEnableEncryptSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
find.descendant(
|
||||
@@ -57,10 +73,10 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
}
|
||||
|
||||
void assertAppFlowyCloudEnableSyncSwitchValue(bool value) {
|
||||
assertSwitchValue(
|
||||
assertToggleValue(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyCloudEnableSync),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Toggle),
|
||||
),
|
||||
value,
|
||||
);
|
||||
@@ -78,7 +94,7 @@ extension AppFlowyAuthTest on WidgetTester {
|
||||
Future<void> toggleEnableSync(Type syncButton) async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(syncButton),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Switch),
|
||||
matching: find.byWidgetPredicate((widget) => widget is Toggle),
|
||||
);
|
||||
|
||||
await tapButton(finder);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/env/cloud_env_test.dart';
|
||||
import 'package:appflowy/startup/entry_point.dart';
|
||||
@@ -13,16 +16,12 @@ import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widget
|
||||
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/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class FlowyTestContext {
|
||||
FlowyTestContext({
|
||||
required this.applicationDataDirectory,
|
||||
});
|
||||
FlowyTestContext({required this.applicationDataDirectory});
|
||||
|
||||
final String applicationDataDirectory;
|
||||
}
|
||||
@@ -75,7 +74,7 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
if (cloudType != null) {
|
||||
switch (cloudType) {
|
||||
case AuthenticatorType.local:
|
||||
await useLocal();
|
||||
await useLocalServer();
|
||||
break;
|
||||
case AuthenticatorType.supabase:
|
||||
await useTestSupabaseCloud();
|
||||
@@ -174,28 +173,29 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
int buttons = kPrimaryButton,
|
||||
bool warnIfMissed = false,
|
||||
int milliseconds = 500,
|
||||
bool pumpAndSettle = true,
|
||||
}) async {
|
||||
await tap(
|
||||
finder,
|
||||
buttons: buttons,
|
||||
warnIfMissed: warnIfMissed,
|
||||
);
|
||||
await pumpAndSettle(
|
||||
Duration(milliseconds: milliseconds),
|
||||
EnginePhase.sendSemanticsUpdate,
|
||||
const Duration(seconds: 5),
|
||||
);
|
||||
|
||||
if (pumpAndSettle) {
|
||||
await this.pumpAndSettle(
|
||||
Duration(milliseconds: milliseconds),
|
||||
EnginePhase.sendSemanticsUpdate,
|
||||
const Duration(seconds: 5),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> tapButtonWithName(
|
||||
String tr, {
|
||||
int milliseconds = 500,
|
||||
bool pumpAndSettle = true,
|
||||
}) async {
|
||||
Finder button = find.text(
|
||||
tr,
|
||||
findRichText: true,
|
||||
skipOffstage: false,
|
||||
);
|
||||
Finder button = find.text(tr, findRichText: true, skipOffstage: false);
|
||||
if (button.evaluate().isEmpty) {
|
||||
button = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == tr,
|
||||
@@ -204,20 +204,8 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
await tapButton(
|
||||
button,
|
||||
milliseconds: milliseconds,
|
||||
pumpAndSettle: pumpAndSettle,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> tapButtonWithTooltip(
|
||||
String tr, {
|
||||
int milliseconds = 500,
|
||||
}) async {
|
||||
final button = find.byTooltip(tr);
|
||||
await tapButton(
|
||||
button,
|
||||
milliseconds: milliseconds,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> doubleTapAt(
|
||||
@@ -232,34 +220,8 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
await pumpAndSettle(Duration(milliseconds: milliseconds));
|
||||
}
|
||||
|
||||
Future<void> doubleTapButton(
|
||||
Finder finder, {
|
||||
int? pointer,
|
||||
int buttons = kPrimaryButton,
|
||||
bool warnIfMissed = true,
|
||||
int milliseconds = 500,
|
||||
}) async {
|
||||
await tap(
|
||||
finder,
|
||||
pointer: pointer,
|
||||
buttons: buttons,
|
||||
warnIfMissed: warnIfMissed,
|
||||
);
|
||||
|
||||
await pump(kDoubleTapMinTime);
|
||||
|
||||
await tap(
|
||||
finder,
|
||||
buttons: buttons,
|
||||
pointer: pointer,
|
||||
warnIfMissed: warnIfMissed,
|
||||
);
|
||||
await pumpAndSettle(Duration(milliseconds: milliseconds));
|
||||
}
|
||||
|
||||
Future<void> wait(int milliseconds) async {
|
||||
await pumpAndSettle(Duration(milliseconds: milliseconds));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,10 +233,6 @@ extension AppFlowyFinderTestBase on CommonFinders {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> useLocal() async {
|
||||
await useLocalServer();
|
||||
}
|
||||
|
||||
Future<void> useTestSupabaseCloud() async {
|
||||
await useSupabaseCloud(
|
||||
url: TestEnv.supabaseUrl,
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
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/mobile/presentation/presentation.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/plugins/shared/share/share_button.dart';
|
||||
import 'package:appflowy/shared/feature_flags.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/sidebar/sidebar_workspace.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_new_page_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.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';
|
||||
@@ -24,12 +21,19 @@ import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_
|
||||
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/settings/shared/settings_body.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/more_view_actions.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/common_view_action.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
|
||||
import 'package:appflowy_backend/log.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/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'emoji.dart';
|
||||
@@ -56,6 +60,7 @@ extension CommonOperations on WidgetTester {
|
||||
/// Tap the + button on the home page.
|
||||
Future<void> tapAddViewButton({
|
||||
String name = gettingStarted,
|
||||
ViewLayoutPB layout = ViewLayoutPB.Document,
|
||||
}) async {
|
||||
await hoverOnPageName(
|
||||
name,
|
||||
@@ -72,27 +77,6 @@ extension CommonOperations on WidgetTester {
|
||||
await tapButton(newPageButton);
|
||||
}
|
||||
|
||||
/// Tap the create document button.
|
||||
///
|
||||
/// Must call [tapAddViewButton] first.
|
||||
Future<void> tapCreateDocumentButton() async {
|
||||
await tapButtonWithName(LocaleKeys.document_menuName.tr());
|
||||
}
|
||||
|
||||
/// Tap the create grid button.
|
||||
///
|
||||
/// Must call [tapAddViewButton] first.
|
||||
Future<void> tapCreateGridButton() async {
|
||||
await tapButtonWithName(LocaleKeys.grid_menuName.tr());
|
||||
}
|
||||
|
||||
/// Tap the create grid button.
|
||||
///
|
||||
/// Must call [tapAddViewButton] first.
|
||||
Future<void> tapCreateCalendarButton() async {
|
||||
await tapButtonWithName(LocaleKeys.calendar_menuName.tr());
|
||||
}
|
||||
|
||||
/// Tap the import button.
|
||||
///
|
||||
/// Must call [tapAddViewButton] first.
|
||||
@@ -181,15 +165,9 @@ extension CommonOperations on WidgetTester {
|
||||
}) async {
|
||||
final pageNames = findPageName(name, layout: layout);
|
||||
if (useLast) {
|
||||
await hoverOnWidget(
|
||||
pageNames.last,
|
||||
onHover: onHover,
|
||||
);
|
||||
await hoverOnWidget(pageNames.last, onHover: onHover);
|
||||
} else {
|
||||
await hoverOnWidget(
|
||||
pageNames.first,
|
||||
onHover: onHover,
|
||||
);
|
||||
await hoverOnWidget(pageNames.first, onHover: onHover);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,7 +258,7 @@ extension CommonOperations on WidgetTester {
|
||||
/// Tap the share button above the document page.
|
||||
Future<void> tapShareButton() async {
|
||||
final shareButton = find.byWidgetPredicate(
|
||||
(widget) => widget is DocumentShareButton,
|
||||
(widget) => widget is ShareButton,
|
||||
);
|
||||
await tapButton(shareButton);
|
||||
}
|
||||
@@ -302,7 +280,7 @@ extension CommonOperations on WidgetTester {
|
||||
bool openAfterCreated = true,
|
||||
}) async {
|
||||
// create a new page
|
||||
await tapAddViewButton(name: parentName ?? gettingStarted);
|
||||
await tapAddViewButton(name: parentName ?? gettingStarted, layout: layout);
|
||||
await tapButtonWithName(layout.menuName);
|
||||
final settingsOrFailure = await getIt<KeyValueStorage>().getWithFormat(
|
||||
KVKeys.showRenameDialogWhenCreatingNewFile,
|
||||
@@ -497,9 +475,7 @@ extension CommonOperations on WidgetTester {
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> openNotificationHub({
|
||||
int tabIndex = 0,
|
||||
}) async {
|
||||
Future<void> openNotificationHub({int tabIndex = 0}) async {
|
||||
final finder = find.descendant(
|
||||
of: find.byType(NotificationButton),
|
||||
matching: find.byWidgetPredicate(
|
||||
@@ -538,17 +514,9 @@ extension CommonOperations on WidgetTester {
|
||||
|
||||
final workspace = find.byType(SidebarWorkspace);
|
||||
expect(workspace, findsOneWidget);
|
||||
// click it
|
||||
await tapButton(workspace, milliseconds: 2000);
|
||||
}
|
||||
|
||||
Future<void> closeCollaborativeWorkspaceMenu() async {
|
||||
if (!FeatureFlag.collaborativeWorkspace.isOn) {
|
||||
throw UnsupportedError('Collaborative workspace is not enabled');
|
||||
}
|
||||
|
||||
await tapAt(Offset.zero);
|
||||
await pumpAndSettle();
|
||||
await tapButton(workspace, pumpAndSettle: false);
|
||||
await pump(const Duration(seconds: 5));
|
||||
}
|
||||
|
||||
Future<void> createCollaborativeWorkspace(String name) async {
|
||||
@@ -563,7 +531,8 @@ extension CommonOperations on WidgetTester {
|
||||
// click the create button
|
||||
final createButton = find.byKey(createWorkspaceButtonKey);
|
||||
expect(createButton, findsOneWidget);
|
||||
await tapButton(createButton);
|
||||
await tapButton(createButton, pumpAndSettle: false);
|
||||
await pump(const Duration(seconds: 5));
|
||||
|
||||
// see the create workspace dialog
|
||||
final createWorkspaceDialog = find.byType(CreateWorkspaceDialog);
|
||||
@@ -572,8 +541,97 @@ extension CommonOperations on WidgetTester {
|
||||
// input the workspace name
|
||||
await enterText(find.byType(TextField), name);
|
||||
|
||||
await tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
await tapButtonWithName(LocaleKeys.button_ok.tr(), pumpAndSettle: false);
|
||||
await pump(const Duration(seconds: 5));
|
||||
}
|
||||
|
||||
// For mobile platform to launch the app in anonymous mode
|
||||
Future<void> launchInAnonymousMode() async {
|
||||
assert(
|
||||
[TargetPlatform.android, TargetPlatform.iOS]
|
||||
.contains(defaultTargetPlatform),
|
||||
'This method is only supported on mobile platforms',
|
||||
);
|
||||
|
||||
await initializeAppFlowy();
|
||||
|
||||
final anonymousSignInButton = find.byType(SignInAnonymousButtonV2);
|
||||
expect(anonymousSignInButton, findsOneWidget);
|
||||
await tapButton(anonymousSignInButton);
|
||||
|
||||
await pumpUntilFound(find.byType(MobileHomeScreen));
|
||||
}
|
||||
|
||||
Future<void> tapSvgButton(FlowySvgData svg) async {
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svg.path == svg.path,
|
||||
);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> openMoreViewActions() async {
|
||||
final button = find.byType(MoreViewActions);
|
||||
await tap(button);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
/// Presses on the Duplicate ViewAction in the [MoreViewActions] popup.
|
||||
///
|
||||
/// [openMoreViewActions] must be called beforehand!
|
||||
///
|
||||
Future<void> duplicateByMoreViewActions() async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(ListView),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewAction && widget.type == ViewActionType.duplicate,
|
||||
),
|
||||
);
|
||||
await tap(button);
|
||||
await pump();
|
||||
}
|
||||
|
||||
/// Presses on the Delete ViewAction in the [MoreViewActions] popup.
|
||||
///
|
||||
/// [openMoreViewActions] must be called beforehand!
|
||||
///
|
||||
Future<void> deleteByMoreViewActions() async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(ListView),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewAction && widget.type == ViewActionType.delete,
|
||||
),
|
||||
);
|
||||
await tap(button);
|
||||
await pump();
|
||||
}
|
||||
}
|
||||
|
||||
extension SettingsFinder on CommonFinders {
|
||||
Finder findSettingsScrollable() => find
|
||||
.descendant(
|
||||
of: find
|
||||
.descendant(
|
||||
of: find.byType(SettingsBody),
|
||||
matching: find.byType(SingleChildScrollView),
|
||||
)
|
||||
.first,
|
||||
matching: find.byType(Scrollable),
|
||||
)
|
||||
.first;
|
||||
|
||||
Finder findSettingsMenuScrollable() => find
|
||||
.descendant(
|
||||
of: find
|
||||
.descendant(
|
||||
of: find.byType(SettingsMenu),
|
||||
matching: find.byType(SingleChildScrollView),
|
||||
)
|
||||
.first,
|
||||
matching: find.byType(Scrollable),
|
||||
)
|
||||
.first;
|
||||
}
|
||||
|
||||
extension ViewLayoutPBTest on ViewLayoutPB {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:archive/archive_io.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@@ -51,11 +52,7 @@ class TestWorkspaceService {
|
||||
Future<void> setUpAll() async {
|
||||
final root = await workspace.root;
|
||||
final path = root.path;
|
||||
SharedPreferences.setMockInitialValues(
|
||||
{
|
||||
KVKeys.pathLocation: path,
|
||||
},
|
||||
);
|
||||
SharedPreferences.setMockInitialValues({KVKeys.pathLocation: path});
|
||||
}
|
||||
|
||||
/// Workspaces that are checked into source are compressed. [TestWorkspaceService.setUp()] decompresses the file into an ephemeral directory that will be ignored by source control.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/database/widgets/field/field_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/field/field_type_list.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -31,8 +29,6 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/discl
|
||||
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/widgets/field/type_option_editor/date/date_time_format.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/field/type_option_editor/number.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';
|
||||
@@ -57,6 +53,10 @@ import 'package:appflowy/plugins/database/widgets/cell_editor/extension.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_cell_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/select_option_text_field.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/database_layout_ext.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/field/field_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/field/field_type_list.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/field/type_option_editor/date/date_time_format.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/field/type_option_editor/number.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_action.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_banner.dart';
|
||||
@@ -71,7 +71,6 @@ import 'package:appflowy/plugins/database/widgets/setting/setting_property_list.
|
||||
import 'package:appflowy/util/field_type_extension.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';
|
||||
@@ -131,12 +130,12 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await openPage('v020', layout: ViewLayoutPB.Grid);
|
||||
}
|
||||
|
||||
Future<void> hoverOnFirstRowOfGrid() async {
|
||||
Future<void> hoverOnFirstRowOfGrid([Future<void> Function()? onHover]) async {
|
||||
final findRow = find.byType(GridRow);
|
||||
expect(findRow, findsWidgets);
|
||||
|
||||
final firstRow = findRow.first;
|
||||
await hoverOnWidget(firstRow);
|
||||
await hoverOnWidget(firstRow, onHover: onHover);
|
||||
}
|
||||
|
||||
Future<void> editCell({
|
||||
@@ -343,16 +342,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
return w.isToday;
|
||||
}
|
||||
|
||||
Future<void> toggleDateRange() async {
|
||||
final findDateEditor = find.byType(EndTimeButton);
|
||||
final findToggle = find.byType(Toggle);
|
||||
final finder = find.descendant(
|
||||
of: findDateEditor,
|
||||
matching: findToggle,
|
||||
);
|
||||
await tapButton(finder);
|
||||
}
|
||||
|
||||
Future<void> tapChangeDateTimeFormatButton() async {
|
||||
await tapButton(find.byType(DateTypeOptionButton));
|
||||
}
|
||||
@@ -403,9 +392,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
/// The [SelectOptionCellEditor] must be opened first.
|
||||
Future<void> createOption({
|
||||
required String name,
|
||||
}) async {
|
||||
Future<void> createOption({required String name}) async {
|
||||
final findEditor = find.byType(SelectOptionCellEditor);
|
||||
expect(findEditor, findsOneWidget);
|
||||
|
||||
@@ -419,9 +406,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> selectOption({
|
||||
required String name,
|
||||
}) async {
|
||||
Future<void> selectOption({required String name}) async {
|
||||
final option = find.byWidgetPredicate(
|
||||
(widget) => widget is SelectOptionTagCell && widget.option.name == name,
|
||||
);
|
||||
@@ -440,11 +425,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
(widget.name == name || widget.option?.name == name),
|
||||
);
|
||||
|
||||
final cell = find.descendant(
|
||||
of: findRow.at(rowIndex),
|
||||
matching: option,
|
||||
);
|
||||
|
||||
final cell = find.descendant(of: findRow.at(rowIndex), matching: option);
|
||||
expect(cell, findsOneWidget);
|
||||
}
|
||||
|
||||
@@ -458,11 +439,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
(widget) => widget is SelectOptionTag,
|
||||
);
|
||||
|
||||
final cell = find.descendant(
|
||||
of: findRow.at(rowIndex),
|
||||
matching: options,
|
||||
);
|
||||
|
||||
final cell = find.descendant(of: findRow.at(rowIndex), matching: options);
|
||||
expect(cell, matcher);
|
||||
}
|
||||
|
||||
@@ -470,21 +447,16 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final findRow = find.byType(GridRow);
|
||||
final findCell = finderForFieldType(FieldType.Checklist);
|
||||
|
||||
final cell = find.descendant(
|
||||
of: findRow.at(rowIndex),
|
||||
matching: findCell,
|
||||
);
|
||||
|
||||
final cell = find.descendant(of: findRow.at(rowIndex), matching: findCell);
|
||||
await tapButton(cell);
|
||||
}
|
||||
|
||||
void assertChecklistEditorVisible({required bool visible}) {
|
||||
final editor = find.byType(ChecklistCellEditor);
|
||||
if (visible) {
|
||||
expect(editor, findsOneWidget);
|
||||
} else {
|
||||
expect(editor, findsNothing);
|
||||
return expect(editor, findsOneWidget);
|
||||
}
|
||||
expect(editor, findsNothing);
|
||||
}
|
||||
|
||||
Future<void> createNewChecklistTask({
|
||||
@@ -519,7 +491,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
required bool isChecked,
|
||||
}) {
|
||||
final task = find.byType(ChecklistItem).at(index);
|
||||
|
||||
final widget = this.widget<ChecklistItem>(task);
|
||||
assert(
|
||||
widget.task.data.name == name && widget.task.isSelected == isChecked,
|
||||
@@ -591,27 +562,16 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> editTitleInRowDetailPage(String title) async {
|
||||
final titleField = find.byType(EditableTextCell);
|
||||
await enterText(titleField, title);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> hoverRowBanner() async {
|
||||
final banner = find.byType(RowBanner);
|
||||
expect(banner, findsOneWidget);
|
||||
|
||||
await startGesture(
|
||||
getCenter(banner),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
|
||||
await startGesture(getCenter(banner), kind: PointerDeviceKind.mouse);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> openEmojiPicker() async {
|
||||
await tapButton(find.byType(AddEmojiButton));
|
||||
}
|
||||
Future<void> openEmojiPicker() async =>
|
||||
tapButton(find.byType(AddEmojiButton));
|
||||
|
||||
Future<void> tapDateCellInRowDetailPage() async {
|
||||
final findDateCell = find.byType(EditableDateCell);
|
||||
@@ -630,25 +590,12 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> duplicateRowInRowDetailPage() async {
|
||||
final duplicateButton = find.byType(RowDetailPageDuplicateButton);
|
||||
await tapButton(duplicateButton);
|
||||
}
|
||||
|
||||
Future<void> deleteRowInRowDetailPage() async {
|
||||
final deleteButton = find.byType(RowDetailPageDeleteButton);
|
||||
await tapButton(deleteButton);
|
||||
}
|
||||
|
||||
Future<TestGesture> hoverOnFieldInRowDetail({required int index}) async {
|
||||
final fieldButtons = find.byType(FieldCellButton);
|
||||
final button = find
|
||||
.descendant(of: find.byType(RowDetailPage), matching: fieldButtons)
|
||||
.at(index);
|
||||
return startGesture(
|
||||
getCenter(button),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
return startGesture(getCenter(button), kind: PointerDeviceKind.mouse);
|
||||
}
|
||||
|
||||
Future<void> reorderFieldInRowDetail({required double offset}) async {
|
||||
@@ -657,11 +604,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
(widget) => widget is ReorderableDragStartListener && widget.enabled,
|
||||
)
|
||||
.first;
|
||||
await drag(
|
||||
thumb,
|
||||
Offset(0, offset),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
await drag(thumb, Offset(0, offset), kind: PointerDeviceKind.mouse);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
@@ -681,8 +624,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
Future<void> tapDeletePropertyInFieldEditor() async {
|
||||
final deleteButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell && widget.action == FieldAction.delete,
|
||||
(w) => w is FieldActionCell && w.action == FieldAction.delete,
|
||||
);
|
||||
await tapButton(deleteButton);
|
||||
|
||||
@@ -693,11 +635,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(confirmButton);
|
||||
}
|
||||
|
||||
Future<void> scrollGridByOffset(Offset offset) async {
|
||||
await drag(find.byType(GridPage), offset);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> scrollRowDetailByOffset(Offset offset) async {
|
||||
await drag(find.byType(RowDetailPage), offset);
|
||||
await pumpAndSettle();
|
||||
@@ -724,10 +661,13 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
Future<void> changeFieldTypeOfFieldWithName(
|
||||
String name,
|
||||
FieldType type,
|
||||
) async {
|
||||
FieldType type, {
|
||||
ViewLayoutPB layout = ViewLayoutPB.Grid,
|
||||
}) async {
|
||||
await tapGridFieldWithName(name);
|
||||
await tapEditFieldButton();
|
||||
if (layout == ViewLayoutPB.Grid) {
|
||||
await tapEditFieldButton();
|
||||
}
|
||||
|
||||
await tapSwitchFieldTypeButton();
|
||||
await selectFieldType(type);
|
||||
@@ -756,8 +696,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
/// Should call [tapGridFieldWithName] first.
|
||||
Future<void> tapDeletePropertyButton() async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell && widget.action == FieldAction.delete,
|
||||
(w) => w is FieldActionCell && w.action == FieldAction.delete,
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
@@ -765,9 +704,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
/// A SimpleDialog must be shown first, e.g. when deleting a field.
|
||||
Future<void> tapDialogOkButton() async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
(w) => w is PrimaryTextButton && w.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
@@ -775,8 +712,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
/// Should call [tapGridFieldWithName] first.
|
||||
Future<void> tapDuplicatePropertyButton() async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell && widget.action == FieldAction.duplicate,
|
||||
(w) => w is FieldActionCell && w.action == FieldAction.duplicate,
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
@@ -798,45 +734,34 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
/// Should call [tapGridFieldWithName] first.
|
||||
Future<void> tapHidePropertyButton() async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell &&
|
||||
widget.action == FieldAction.toggleVisibility,
|
||||
(w) => w is FieldActionCell && w.action == FieldAction.toggleVisibility,
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
|
||||
Future<void> tapHidePropertyButtonInFieldEditor() async {
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldActionCell &&
|
||||
widget.action == FieldAction.toggleVisibility,
|
||||
(w) => w is FieldActionCell && w.action == FieldAction.toggleVisibility,
|
||||
);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> tapRowDetailPageRowActionButton() async {
|
||||
await tapButton(find.byType(RowActionButton));
|
||||
}
|
||||
Future<void> tapRowDetailPageRowActionButton() async =>
|
||||
tapButton(find.byType(RowActionButton));
|
||||
|
||||
Future<void> tapRowDetailPageCreatePropertyButton() async {
|
||||
await tapButton(find.byType(CreateRowFieldButton));
|
||||
}
|
||||
Future<void> tapRowDetailPageCreatePropertyButton() async =>
|
||||
tapButton(find.byType(CreateRowFieldButton));
|
||||
|
||||
Future<void> tapRowDetailPageDeleteRowButton() async {
|
||||
await tapButton(find.byType(RowDetailPageDeleteButton));
|
||||
}
|
||||
Future<void> tapRowDetailPageDeleteRowButton() async =>
|
||||
tapButton(find.byType(RowDetailPageDeleteButton));
|
||||
|
||||
Future<void> tapRowDetailPageDuplicateRowButton() async {
|
||||
await tapButton(find.byType(RowDetailPageDuplicateButton));
|
||||
}
|
||||
Future<void> tapRowDetailPageDuplicateRowButton() async =>
|
||||
tapButton(find.byType(RowDetailPageDuplicateButton));
|
||||
|
||||
Future<void> tapSwitchFieldTypeButton() async {
|
||||
await tapButton(find.byType(SwitchFieldButton));
|
||||
}
|
||||
Future<void> tapSwitchFieldTypeButton() async =>
|
||||
tapButton(find.byType(SwitchFieldButton));
|
||||
|
||||
Future<void> tapEscButton() async {
|
||||
await sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
}
|
||||
Future<void> tapEscButton() async => sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
|
||||
/// Must call [tapSwitchFieldTypeButton] first.
|
||||
Future<void> selectFieldType(FieldType fieldType) async {
|
||||
@@ -851,15 +776,13 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
// Use in edit mode of FieldEditor
|
||||
void expectEmptyTypeOptionEditor() {
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(FieldTypeOptionEditor),
|
||||
matching: find.byType(TypeOptionSeparator),
|
||||
),
|
||||
findsNothing,
|
||||
);
|
||||
}
|
||||
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.
|
||||
@@ -868,10 +791,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
expect(finder, findsWidgets);
|
||||
}
|
||||
|
||||
Future<void> assertNumberOfFieldsInGridPage(int num) async {
|
||||
expect(find.byType(GridFieldCell), findsNWidgets(num));
|
||||
}
|
||||
|
||||
Future<void> assertNumberOfRowsInGridPage(int num) async {
|
||||
expect(
|
||||
find.byType(GridRow, skipOffstage: false),
|
||||
@@ -884,14 +803,11 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
/// Check the field type of the [FieldCellButton] is the same as the name.
|
||||
Future<void> assertFieldTypeWithFieldName(
|
||||
String name,
|
||||
FieldType fieldType,
|
||||
) async {
|
||||
Future<void> assertFieldTypeWithFieldName(String name, FieldType type) async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FieldCellButton &&
|
||||
widget.field.fieldType == fieldType &&
|
||||
widget.field.fieldType == type &&
|
||||
widget.field.name == name,
|
||||
);
|
||||
|
||||
@@ -936,11 +852,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
|
||||
Future<void> findFieldEditor(dynamic matcher) async {
|
||||
final finder = find.byType(FieldEditor);
|
||||
expect(finder, matcher);
|
||||
}
|
||||
|
||||
Future<void> findDateEditor(dynamic matcher) async {
|
||||
final finder = find.byType(DateCellEditor);
|
||||
expect(finder, matcher);
|
||||
@@ -965,16 +876,24 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> tapRowMenuButtonInGrid() async {
|
||||
expect(find.byType(RowMenuButton), findsOneWidget);
|
||||
await tapButton(find.byType(RowMenuButton));
|
||||
}
|
||||
|
||||
/// Should call [tapRowMenuButtonInGrid] first.
|
||||
Future<void> tapDeleteOnRowMenu() async {
|
||||
expect(find.text(LocaleKeys.grid_row_delete.tr()), findsOneWidget);
|
||||
await tapButtonWithName(LocaleKeys.grid_row_delete.tr());
|
||||
}
|
||||
|
||||
Future<void> createField(FieldType fieldType, String name) async {
|
||||
await scrollToRight(find.byType(GridPage));
|
||||
Future<void> createField(
|
||||
FieldType fieldType,
|
||||
String name, {
|
||||
ViewLayoutPB layout = ViewLayoutPB.Grid,
|
||||
}) async {
|
||||
if (layout == ViewLayoutPB.Grid) {
|
||||
await scrollToRight(find.byType(GridPage));
|
||||
}
|
||||
await tapNewPropertyButton();
|
||||
await renameField(name);
|
||||
await tapSwitchFieldTypeButton();
|
||||
@@ -994,41 +913,29 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(find.byType(SortButton));
|
||||
}
|
||||
|
||||
Future<void> tapCreateFilterByFieldType(
|
||||
FieldType fieldType,
|
||||
String title,
|
||||
) async {
|
||||
Future<void> tapCreateFilterByFieldType(FieldType type, String title) async {
|
||||
final findFilter = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is GridFilterPropertyCell &&
|
||||
widget.fieldInfo.fieldType == fieldType &&
|
||||
widget.fieldInfo.fieldType == type &&
|
||||
widget.fieldInfo.name == title,
|
||||
);
|
||||
|
||||
await tapButton(findFilter);
|
||||
}
|
||||
|
||||
Future<void> tapFilterButtonInGrid(String filterName) async {
|
||||
Future<void> tapFilterButtonInGrid(String name) async {
|
||||
final findFilter = find.byType(FilterMenuItem);
|
||||
final button = find.descendant(
|
||||
of: findFilter,
|
||||
matching: find.text(filterName),
|
||||
);
|
||||
|
||||
final button = find.descendant(of: findFilter, matching: find.text(name));
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> tapCreateSortByFieldType(
|
||||
FieldType fieldType,
|
||||
String title,
|
||||
) async {
|
||||
Future<void> tapCreateSortByFieldType(FieldType type, String title) async {
|
||||
final findSort = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is GridSortPropertyCell &&
|
||||
widget.fieldInfo.fieldType == fieldType &&
|
||||
widget.fieldInfo.fieldType == type &&
|
||||
widget.fieldInfo.name == title,
|
||||
);
|
||||
|
||||
await tapButton(findSort);
|
||||
}
|
||||
|
||||
@@ -1085,10 +992,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
of: fromSortItem,
|
||||
matching: find.byType(ReorderableDragStartListener),
|
||||
);
|
||||
await drag(
|
||||
dragElement,
|
||||
getCenter(toSortItem) - getCenter(fromSortItem),
|
||||
);
|
||||
await drag(dragElement, getCenter(toSortItem) - getCenter(fromSortItem));
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
|
||||
@@ -1166,15 +1070,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(findCell);
|
||||
}
|
||||
|
||||
Future<void> tapCheckedButtonOnCheckboxFilter() async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(HoverButton),
|
||||
matching: find.text(LocaleKeys.grid_checkboxFilter_isChecked.tr()),
|
||||
);
|
||||
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> tapUnCheckedButtonOnCheckboxFilter() async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(HoverButton),
|
||||
@@ -1193,15 +1088,6 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> tapUnCompletedButtonOnChecklistFilter() async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(HoverButton),
|
||||
matching: find.text(LocaleKeys.grid_checklistFilter_isIncomplted.tr()),
|
||||
);
|
||||
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
/// Should call [tapDatabaseSettingButton] first.
|
||||
Future<void> tapViewPropertiesButton() async {
|
||||
final findSettingItem = find.byType(DatabaseSettingsList);
|
||||
@@ -1252,16 +1138,8 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> tapFirstDayOfWeek() async {
|
||||
await tapButton(find.byType(FirstDayOfWeek));
|
||||
}
|
||||
|
||||
Future<void> tapFirstDayOfWeekStartFromSunday() async {
|
||||
final finder = find.byWidgetPredicate(
|
||||
(widget) => widget is StartFromButton && widget.dayIndex == 0,
|
||||
);
|
||||
await tapButton(finder);
|
||||
}
|
||||
Future<void> tapFirstDayOfWeek() async =>
|
||||
tapButton(find.byType(FirstDayOfWeek));
|
||||
|
||||
Future<void> tapFirstDayOfWeekStartFromMonday() async {
|
||||
final finder = find.byWidgetPredicate(
|
||||
@@ -1277,20 +1155,14 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
void assertFirstDayOfWeekStartFromMonday() {
|
||||
final finder = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is StartFromButton &&
|
||||
widget.dayIndex == 1 &&
|
||||
widget.isSelected == true,
|
||||
(w) => w is StartFromButton && w.dayIndex == 1 && w.isSelected == true,
|
||||
);
|
||||
expect(finder, findsOneWidget);
|
||||
}
|
||||
|
||||
void assertFirstDayOfWeekStartFromSunday() {
|
||||
final finder = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is StartFromButton &&
|
||||
widget.dayIndex == 0 &&
|
||||
widget.isSelected == true,
|
||||
(w) => w is StartFromButton && w.dayIndex == 0 && w.isSelected == true,
|
||||
);
|
||||
expect(finder, findsOneWidget);
|
||||
}
|
||||
@@ -1307,11 +1179,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
),
|
||||
)
|
||||
.first;
|
||||
await scrollUntilVisible(
|
||||
todayCell,
|
||||
300,
|
||||
scrollable: scrollable,
|
||||
);
|
||||
await scrollUntilVisible(todayCell, 300, scrollable: scrollable);
|
||||
await pumpAndSettle(const Duration(milliseconds: 300));
|
||||
}
|
||||
|
||||
@@ -1351,12 +1219,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
String? title,
|
||||
}) {
|
||||
final findDayCell = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is CalendarDayCard &&
|
||||
isSameDay(
|
||||
widget.date,
|
||||
date,
|
||||
),
|
||||
(widget) => widget is CalendarDayCard && isSameDay(widget.date, date),
|
||||
);
|
||||
Finder findEvents = find.descendant(
|
||||
of: findDayCell,
|
||||
@@ -1390,13 +1253,11 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(cards.at(index));
|
||||
}
|
||||
|
||||
void assertEventEditorOpen() {
|
||||
expect(find.byType(CalendarEventEditor), findsOneWidget);
|
||||
}
|
||||
void assertEventEditorOpen() =>
|
||||
expect(find.byType(CalendarEventEditor), findsOneWidget);
|
||||
|
||||
Future<void> dismissEventEditor() async {
|
||||
await simulateKeyEvent(LogicalKeyboardKey.escape);
|
||||
}
|
||||
Future<void> dismissEventEditor() async =>
|
||||
simulateKeyEvent(LogicalKeyboardKey.escape);
|
||||
|
||||
Future<void> editEventTitle(String title) async {
|
||||
final textField = find.descendant(
|
||||
@@ -1507,10 +1368,9 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
if (isVisible) {
|
||||
expect(textField, findsOneWidget);
|
||||
} else {
|
||||
expect(textField, findsNothing);
|
||||
return expect(textField, findsOneWidget);
|
||||
}
|
||||
expect(textField, findsNothing);
|
||||
}
|
||||
|
||||
Future<void> enterNewGroupName(String name, {required bool submit}) async {
|
||||
@@ -1612,21 +1472,14 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(okButton);
|
||||
}
|
||||
|
||||
void assertCurrentDatabaseTagIs(DatabaseLayoutPB layout) {
|
||||
switch (layout) {
|
||||
case DatabaseLayoutPB.Board:
|
||||
expect(find.byType(BoardPage), findsOneWidget);
|
||||
break;
|
||||
case DatabaseLayoutPB.Calendar:
|
||||
expect(find.byType(CalendarPage), findsOneWidget);
|
||||
break;
|
||||
case DatabaseLayoutPB.Grid:
|
||||
expect(find.byType(GridPage), findsOneWidget);
|
||||
break;
|
||||
default:
|
||||
throw Exception('Unknown database layout type: $layout');
|
||||
}
|
||||
}
|
||||
void assertCurrentDatabaseTagIs(DatabaseLayoutPB layout) => switch (layout) {
|
||||
DatabaseLayoutPB.Board =>
|
||||
expect(find.byType(DesktopBoardPage), findsOneWidget),
|
||||
DatabaseLayoutPB.Calendar =>
|
||||
expect(find.byType(CalendarPage), findsOneWidget),
|
||||
DatabaseLayoutPB.Grid => expect(find.byType(GridPage), findsOneWidget),
|
||||
_ => throw Exception('Unknown database layout type: $layout'),
|
||||
};
|
||||
|
||||
Future<void> selectDatabaseLayoutType(DatabaseLayoutPB layout) async {
|
||||
final findLayoutCell = find.byType(DatabaseViewLayoutCell);
|
||||
@@ -1634,11 +1487,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
(widget) => widget is FlowyText && widget.text == layout.layoutName,
|
||||
);
|
||||
|
||||
final button = find.descendant(
|
||||
of: findLayoutCell,
|
||||
matching: findText,
|
||||
);
|
||||
|
||||
final button = find.descendant(of: findLayoutCell, matching: findText);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
@@ -1660,8 +1509,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
await tapButton(
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is NumberFormatCell && widget.format == NumberFormatPB.USD,
|
||||
(w) => w is NumberFormatCell && w.format == NumberFormatPB.USD,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1675,8 +1523,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
String fieldName,
|
||||
) async {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is DatabasePropertyCell && widget.fieldInfo.name == fieldName,
|
||||
(w) => w is DatabasePropertyCell && w.fieldInfo.name == fieldName,
|
||||
);
|
||||
final toggleVisibilityButton =
|
||||
find.descendant(of: field, matching: find.byType(FlowyIconButton));
|
||||
@@ -1684,18 +1531,12 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
}
|
||||
|
||||
Finder finderForDatabaseLayoutType(DatabaseLayoutPB layout) {
|
||||
switch (layout) {
|
||||
case DatabaseLayoutPB.Board:
|
||||
return find.byType(BoardPage);
|
||||
case DatabaseLayoutPB.Calendar:
|
||||
return find.byType(CalendarPage);
|
||||
case DatabaseLayoutPB.Grid:
|
||||
return find.byType(GridPage);
|
||||
default:
|
||||
throw Exception('Unknown database layout type: $layout');
|
||||
}
|
||||
}
|
||||
Finder finderForDatabaseLayoutType(DatabaseLayoutPB layout) => switch (layout) {
|
||||
DatabaseLayoutPB.Board => find.byType(DesktopBoardPage),
|
||||
DatabaseLayoutPB.Calendar => find.byType(CalendarPage),
|
||||
DatabaseLayoutPB.Grid => find.byType(GridPage),
|
||||
_ => throw Exception('Unknown database layout type: $layout'),
|
||||
};
|
||||
|
||||
Finder finderForFieldType(FieldType fieldType) {
|
||||
switch (fieldType) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'dart:io';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
Future<void> deleteDirectoriesWithSameBaseNameAsPrefix(
|
||||
String path,
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/bl
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/cover_editor.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/image/embed_image_url_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/upload_image_menu/widgets/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';
|
||||
@@ -31,11 +31,8 @@ class EditorOperations {
|
||||
|
||||
final WidgetTester tester;
|
||||
|
||||
EditorState getCurrentEditorState() {
|
||||
return tester
|
||||
.widget<AppFlowyEditor>(find.byType(AppFlowyEditor))
|
||||
.editorState;
|
||||
}
|
||||
EditorState getCurrentEditorState() =>
|
||||
tester.widget<AppFlowyEditor>(find.byType(AppFlowyEditor)).editorState;
|
||||
|
||||
/// Tap the line of editor at [index]
|
||||
Future<void> tapLineOfEditorAt(int index) async {
|
||||
@@ -66,7 +63,7 @@ class EditorOperations {
|
||||
Future<void> tapGettingStartedIcon() async {
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(DocumentHeaderNodeWidget),
|
||||
of: find.byType(DocumentCoverWidget),
|
||||
matching: find.findTextInFlowyText('⭐️'),
|
||||
),
|
||||
);
|
||||
@@ -85,15 +82,12 @@ class EditorOperations {
|
||||
|
||||
/// Taps the 'Remove Icon' button in the cover toolbar and the icon popover
|
||||
Future<void> tapRemoveIconButton({bool isInPicker = false}) async {
|
||||
Finder button =
|
||||
find.text(LocaleKeys.document_plugins_cover_removeIcon.tr());
|
||||
if (isInPicker) {
|
||||
button = find.descendant(
|
||||
of: find.byType(FlowyIconPicker),
|
||||
matching: button,
|
||||
);
|
||||
}
|
||||
|
||||
final Finder button = !isInPicker
|
||||
? find.text(LocaleKeys.document_plugins_cover_removeIcon.tr())
|
||||
: find.descendant(
|
||||
of: find.byType(FlowyIconPicker),
|
||||
matching: find.text(LocaleKeys.button_remove.tr()),
|
||||
);
|
||||
await tester.tapButton(button);
|
||||
}
|
||||
|
||||
@@ -145,16 +139,8 @@ class EditorOperations {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> switchNetworkImageCover(String imageUrl) async {
|
||||
final image = find.byWidgetPredicate(
|
||||
(widget) => widget is ImageGridItem,
|
||||
);
|
||||
await tester.tapButton(image);
|
||||
}
|
||||
|
||||
Future<void> tapOnRemoveCover() async {
|
||||
await tester.tapButton(find.byType(DeleteCoverButton));
|
||||
}
|
||||
Future<void> tapOnRemoveCover() async =>
|
||||
tester.tapButton(find.byType(DeleteCoverButton));
|
||||
|
||||
/// A cover must be present in the document to function properly since this
|
||||
/// catches all cover types collectively
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/banner.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart';
|
||||
@@ -10,6 +11,7 @@ import 'package:appflowy/workspace/presentation/notifications/widgets/notificati
|
||||
import 'package:appflowy/workspace/presentation/widgets/date_picker/widgets/reminder_selector.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.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:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -89,18 +91,6 @@ extension Expectation on WidgetTester {
|
||||
expect(exportSuccess, findsOneWidget);
|
||||
}
|
||||
|
||||
/// Expect to see the add button and icon button in the cover toolbar
|
||||
void expectToSeePluginAddCoverAndIconButton() {
|
||||
final addCover = find.textContaining(
|
||||
LocaleKeys.document_plugins_cover_addCover.tr(),
|
||||
);
|
||||
final addIcon = find.textContaining(
|
||||
LocaleKeys.document_plugins_cover_addIcon.tr(),
|
||||
);
|
||||
expect(addCover, findsOneWidget);
|
||||
expect(addIcon, findsOneWidget);
|
||||
}
|
||||
|
||||
/// Expect to see the document header toolbar empty
|
||||
void expectToSeeEmptyDocumentHeaderToolbar() {
|
||||
final addCover = find.textContaining(
|
||||
@@ -153,14 +143,6 @@ extension Expectation on WidgetTester {
|
||||
expect(findRemoveIcon, findsOneWidget);
|
||||
}
|
||||
|
||||
/// Expect to see the user name on the home page
|
||||
void expectToSeeUserName(String name) {
|
||||
final userName = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == name,
|
||||
);
|
||||
expect(userName, findsOneWidget);
|
||||
}
|
||||
|
||||
/// Expect to see a text
|
||||
void expectToSeeText(String text) {
|
||||
Finder textWidget = find.textContaining(text, findRichText: true);
|
||||
@@ -178,26 +160,23 @@ extension Expectation on WidgetTester {
|
||||
ViewLayoutPB layout = ViewLayoutPB.Document,
|
||||
String? parentName,
|
||||
ViewLayoutPB parentLayout = ViewLayoutPB.Document,
|
||||
}) {
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite &&
|
||||
widget.view.name == name &&
|
||||
widget.view.layout == layout,
|
||||
skipOffstage: false,
|
||||
);
|
||||
}
|
||||
}) =>
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.spaceType == FolderSpaceType.favorite &&
|
||||
widget.view.name == name &&
|
||||
widget.view.layout == layout,
|
||||
skipOffstage: false,
|
||||
);
|
||||
|
||||
Finder findAllFavoritePages() {
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.categoryType == FolderCategoryType.favorite,
|
||||
);
|
||||
}
|
||||
Finder findAllFavoritePages() => find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.isFavorite &&
|
||||
widget.spaceType == FolderSpaceType.favorite,
|
||||
);
|
||||
|
||||
Finder findPageName(
|
||||
String name, {
|
||||
@@ -205,25 +184,35 @@ extension Expectation on WidgetTester {
|
||||
String? parentName,
|
||||
ViewLayoutPB parentLayout = ViewLayoutPB.Document,
|
||||
}) {
|
||||
if (parentName == null) {
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.name == name &&
|
||||
widget.view.layout == layout,
|
||||
skipOffstage: false,
|
||||
if (PlatformExtension.isDesktop) {
|
||||
if (parentName == null) {
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SingleInnerViewItem &&
|
||||
widget.view.name == name &&
|
||||
widget.view.layout == layout,
|
||||
skipOffstage: false,
|
||||
);
|
||||
}
|
||||
|
||||
return find.descendant(
|
||||
of: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is InnerViewItem &&
|
||||
widget.view.name == parentName &&
|
||||
widget.view.layout == parentLayout,
|
||||
skipOffstage: false,
|
||||
),
|
||||
matching: findPageName(name, layout: layout),
|
||||
);
|
||||
}
|
||||
|
||||
return find.descendant(
|
||||
of: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is InnerViewItem &&
|
||||
widget.view.name == parentName &&
|
||||
widget.view.layout == parentLayout,
|
||||
skipOffstage: false,
|
||||
),
|
||||
matching: findPageName(name, layout: layout),
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SingleMobileInnerViewItem &&
|
||||
widget.view.name == name &&
|
||||
widget.view.layout == layout,
|
||||
skipOffstage: false,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,18 @@ class FlowyTestKeyboard {
|
||||
static Future<void> simulateKeyDownEvent(
|
||||
List<LogicalKeyboardKey> keys, {
|
||||
required flutter_test.WidgetTester tester,
|
||||
bool withKeyUp = false,
|
||||
}) async {
|
||||
for (final LogicalKeyboardKey key in keys) {
|
||||
await flutter_test.simulateKeyDownEvent(key);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
if (withKeyUp) {
|
||||
for (final LogicalKeyboardKey key in keys) {
|
||||
await flutter_test.simulateKeyUpEvent(key);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,7 @@ class MockFilePicker implements FilePickerService {
|
||||
final List<String> mockPaths;
|
||||
|
||||
@override
|
||||
Future<String?> getDirectoryPath({String? title}) {
|
||||
return Future.value(mockPath);
|
||||
}
|
||||
Future<String?> getDirectoryPath({String? title}) => Future.value(mockPath);
|
||||
|
||||
@override
|
||||
Future<String?> saveFile({
|
||||
@@ -23,9 +21,8 @@ class MockFilePicker implements FilePickerService {
|
||||
FileType type = FileType.any,
|
||||
List<String>? allowedExtensions,
|
||||
bool lockParentWindow = false,
|
||||
}) {
|
||||
return Future.value(mockPath);
|
||||
}
|
||||
}) =>
|
||||
Future.value(mockPath);
|
||||
|
||||
@override
|
||||
Future<FilePickerResult?> pickFiles({
|
||||
@@ -42,34 +39,21 @@ class MockFilePicker implements FilePickerService {
|
||||
}) {
|
||||
final platformFiles =
|
||||
mockPaths.map((e) => PlatformFile(path: e, name: '', size: 0)).toList();
|
||||
return Future.value(
|
||||
FilePickerResult(
|
||||
platformFiles,
|
||||
),
|
||||
);
|
||||
return Future.value(FilePickerResult(platformFiles));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> mockGetDirectoryPath(
|
||||
String path,
|
||||
) async {
|
||||
Future<void> mockGetDirectoryPath(String path) async {
|
||||
getIt.unregister<FilePickerService>();
|
||||
getIt.registerFactory<FilePickerService>(
|
||||
() => MockFilePicker(
|
||||
mockPath: path,
|
||||
),
|
||||
() => MockFilePicker(mockPath: path),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<String> mockSaveFilePath(
|
||||
String path,
|
||||
) async {
|
||||
Future<String> mockSaveFilePath(String path) async {
|
||||
getIt.unregister<FilePickerService>();
|
||||
getIt.registerFactory<FilePickerService>(
|
||||
() => MockFilePicker(
|
||||
mockPath: path,
|
||||
),
|
||||
() => MockFilePicker(mockPath: path),
|
||||
);
|
||||
return path;
|
||||
}
|
||||
@@ -77,9 +61,7 @@ Future<String> mockSaveFilePath(
|
||||
List<String> mockPickFilePaths({required List<String> paths}) {
|
||||
getIt.unregister<FilePickerService>();
|
||||
getIt.registerFactory<FilePickerService>(
|
||||
() => MockFilePicker(
|
||||
mockPaths: paths,
|
||||
),
|
||||
() => MockFilePicker(mockPaths: paths),
|
||||
);
|
||||
return paths;
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'dart:convert';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/text_completion.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/error.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/error.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/openai_client.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/service/text_completion.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
|
||||
class MyMockClient extends Mock implements http.Client {
|
||||
@override
|
||||
@@ -43,7 +44,7 @@ class MockOpenAIRepository extends HttpOpenAIRepository {
|
||||
required Future<void> Function() onStart,
|
||||
required Future<void> Function(TextCompletionResponse response) onProcess,
|
||||
required Future<void> Function() onEnd,
|
||||
required void Function(OpenAIError error) onError,
|
||||
required void Function(AIError error) onError,
|
||||
String? suffix,
|
||||
int maxTokens = 2048,
|
||||
double temperature = 0.3,
|
||||
@@ -52,7 +53,7 @@ class MockOpenAIRepository extends HttpOpenAIRepository {
|
||||
final request = http.Request('POST', OpenAIRequestType.textCompletion.uri);
|
||||
final response = await client.send(request);
|
||||
|
||||
var previousSyntax = '';
|
||||
String previousSyntax = '';
|
||||
if (response.statusCode == 200) {
|
||||
await for (final chunk in response.stream
|
||||
.transform(const Utf8Decoder())
|
||||
@@ -76,6 +77,5 @@ class MockOpenAIRepository extends HttpOpenAIRepository {
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,7 @@ class MockUrlLauncher extends Fake
|
||||
bool launchCalled = false;
|
||||
|
||||
// ignore: use_setters_to_change_properties
|
||||
void setCanLaunchExpectations(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
void setCanLaunchExpectations(String url) => this.url = url;
|
||||
|
||||
void setLaunchExpectations({
|
||||
required String url,
|
||||
@@ -53,10 +51,7 @@ class MockUrlLauncher extends Fake
|
||||
this.webOnlyWindowName = webOnlyWindowName;
|
||||
}
|
||||
|
||||
// ignore: use_setters_to_change_properties
|
||||
void setResponse(bool response) {
|
||||
this.response = response;
|
||||
}
|
||||
void setResponse(bool response) => this.response = response;
|
||||
|
||||
@override
|
||||
LinkDelegate? get linkDelegate => null;
|
||||
@@ -104,7 +99,5 @@ class MockUrlLauncher extends Fake
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> closeWebView() async {
|
||||
closeWebViewCalled = true;
|
||||
}
|
||||
Future<void> closeWebView() async => closeWebViewCalled = true;
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/prelude.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_setting.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_setting.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_workspace_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/direction_setting.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text_field.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../desktop/board/board_hide_groups_test.dart';
|
||||
|
||||
import 'base.dart';
|
||||
import 'common_operations.dart';
|
||||
|
||||
extension AppFlowySettings on WidgetTester {
|
||||
/// Open settings page
|
||||
@@ -26,32 +32,29 @@ extension AppFlowySettings on WidgetTester {
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) => widget is SettingsMenuElement && widget.page == page,
|
||||
);
|
||||
|
||||
await scrollUntilVisible(
|
||||
button,
|
||||
0,
|
||||
scrollable: find.findSettingsMenuScrollable(),
|
||||
);
|
||||
await pump();
|
||||
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
return;
|
||||
}
|
||||
|
||||
Future<void> expectNoSettingsPage(SettingsPage page) async {
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) => widget is SettingsMenuElement && widget.page == page,
|
||||
);
|
||||
expect(button, findsNothing);
|
||||
return;
|
||||
}
|
||||
|
||||
/// Restore the AppFlowy data storage location
|
||||
Future<void> restoreLocation() async {
|
||||
final button =
|
||||
find.byTooltip(LocaleKeys.settings_files_recoverLocationTooltips.tr());
|
||||
final button = find.text(LocaleKeys.settings_common_reset.tr());
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
return;
|
||||
}
|
||||
await pumpAndSettle();
|
||||
|
||||
Future<void> tapOpenFolderButton() async {
|
||||
final button = find.text(LocaleKeys.settings_files_open.tr());
|
||||
expect(button, findsOneWidget);
|
||||
await tapButton(button);
|
||||
final confirmButton = find.text(LocaleKeys.button_confirm.tr());
|
||||
expect(confirmButton, findsOneWidget);
|
||||
await tapButton(confirmButton);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,24 +69,43 @@ extension AppFlowySettings on WidgetTester {
|
||||
|
||||
/// Enter user name
|
||||
Future<void> enterUserName(String name) async {
|
||||
final uni = find.byType(UserNameInput);
|
||||
expect(uni, findsOneWidget);
|
||||
await tap(uni);
|
||||
await enterText(uni, name);
|
||||
await wait(300); //
|
||||
await testTextInput.receiveAction(TextInputAction.done);
|
||||
// Enable editing username
|
||||
final editUsernameFinder = find.descendant(
|
||||
of: find.byType(UserProfileSetting),
|
||||
matching: find.byFlowySvg(FlowySvgs.edit_s),
|
||||
);
|
||||
await tap(editUsernameFinder);
|
||||
await pumpAndSettle();
|
||||
|
||||
final userNameFinder = find.descendant(
|
||||
of: find.byType(UserProfileSetting),
|
||||
matching: find.byType(FlowyTextField),
|
||||
);
|
||||
await enterText(userNameFinder, name);
|
||||
await pumpAndSettle();
|
||||
|
||||
await tap(find.text(LocaleKeys.button_save.tr()));
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
// go to settings page and toggle enable RTL toolbar items
|
||||
Future<void> toggleEnableRTLToolbarItems() async {
|
||||
await openSettings();
|
||||
await openSettingsPage(SettingsPage.appearance);
|
||||
await openSettingsPage(SettingsPage.workspace);
|
||||
|
||||
final switchButton =
|
||||
find.byKey(EnableRTLToolbarItemsSetting.enableRTLSwitchKey);
|
||||
expect(switchButton, findsOneWidget);
|
||||
await tapButton(switchButton);
|
||||
final scrollable = find.findSettingsScrollable();
|
||||
await scrollUntilVisible(
|
||||
find.byType(EnableRTLItemsSwitcher),
|
||||
0,
|
||||
scrollable: scrollable,
|
||||
);
|
||||
|
||||
final switcher = find.descendant(
|
||||
of: find.byType(EnableRTLItemsSwitcher),
|
||||
matching: find.byType(Toggle),
|
||||
);
|
||||
|
||||
await tap(switcher);
|
||||
|
||||
// tap anywhere to close the settings page
|
||||
await tapAt(Offset.zero);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_workspace.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'base.dart';
|
||||
import 'util.dart';
|
||||
|
||||
extension AppFlowyWorkspace on WidgetTester {
|
||||
/// Open workspace menu
|
||||
@@ -36,12 +36,19 @@ extension AppFlowyWorkspace on WidgetTester {
|
||||
matching: find.byType(WorkspaceMoreActionList),
|
||||
);
|
||||
expect(moreButton, findsOneWidget);
|
||||
await tapButton(moreButton);
|
||||
await tapButton(find.findTextInFlowyText(LocaleKeys.button_rename.tr()));
|
||||
final input = find.byType(TextFormField);
|
||||
expect(input, findsOneWidget);
|
||||
await enterText(input, name);
|
||||
await tapButton(find.text(LocaleKeys.button_ok.tr()));
|
||||
await hoverOnWidget(
|
||||
moreButton,
|
||||
onHover: () async {
|
||||
await tapButton(moreButton);
|
||||
await tapButton(
|
||||
find.findTextInFlowyText(LocaleKeys.button_rename.tr()),
|
||||
);
|
||||
final input = find.byType(TextFormField);
|
||||
expect(input, findsOneWidget);
|
||||
await enterText(input, name);
|
||||
await tapButton(find.text(LocaleKeys.button_ok.tr()));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> changeWorkspaceIcon(String icon) async {
|
||||
|
||||
@@ -37,6 +37,16 @@ end
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
|
||||
target.build_configurations.each do |config|
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
|
||||
'$(inherited)',
|
||||
|
||||
# dart: PermissionGroup.photos
|
||||
'PERMISSION_PHOTOS=1',
|
||||
]
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
installer.aggregate_targets.each do |target|
|
||||
|
||||
@@ -63,9 +63,11 @@ PODS:
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- ReachabilitySwift (5.0.0)
|
||||
- rich_clipboard_ios (0.0.1):
|
||||
- permission_handler_apple (9.3.0):
|
||||
- Flutter
|
||||
- printing (1.0.0):
|
||||
- Flutter
|
||||
- ReachabilitySwift (5.0.0)
|
||||
- SDWebImage (5.14.2):
|
||||
- SDWebImage/Core (= 5.14.2)
|
||||
- SDWebImage/Core (5.14.2)
|
||||
@@ -100,7 +102,8 @@ DEPENDENCIES:
|
||||
- keyboard_height_plugin (from `.symlinks/plugins/keyboard_height_plugin/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- rich_clipboard_ios (from `.symlinks/plugins/rich_clipboard_ios/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- printing (from `.symlinks/plugins/printing/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/darwin`)
|
||||
@@ -147,8 +150,10 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
rich_clipboard_ios:
|
||||
:path: ".symlinks/plugins/rich_clipboard_ios/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
printing:
|
||||
:path: ".symlinks/plugins/printing/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
shared_preferences_foundation:
|
||||
@@ -164,22 +169,23 @@ SPEC CHECKSUMS:
|
||||
app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
|
||||
appflowy_backend: 144c20d8bfb298c4e10fa3fa6701a9f41bf98b88
|
||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
||||
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||
DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179
|
||||
file_picker: 15fd9539e4eb735dc54bae8c0534a7a9511a03de
|
||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
|
||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||
integration_test: 13825b8a9334a850581300559b8839134b124670
|
||||
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
||||
irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9
|
||||
keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86
|
||||
package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
printing: 233e1b73bd1f4a05615548e9b5a324c98588640b
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
rich_clipboard_ios: 7588abe18f881a6d0e9ec0b12e51cae2761e8942
|
||||
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
||||
@@ -189,6 +195,6 @@ SPEC CHECKSUMS:
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||
|
||||
PODFILE CHECKSUM: d94f9be27d1db182e9bc77d10f065555d518f127
|
||||
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
||||
|
||||
COCOAPODS: 1.15.2
|
||||
@@ -127,6 +127,7 @@
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
08FAA63113168DEC7FB74204 /* [CP] Embed Pods Frameworks */,
|
||||
A548E58D5F4006A34D7DAA88 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -233,6 +234,23 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
|
||||
};
|
||||
A548E58D5F4006A34D7DAA88 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
E790B8FE5609053209ED85CB /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
||||
@@ -66,5 +66,10 @@
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false />
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -18,6 +18,13 @@ class KVKeys {
|
||||
/// {'dx': 10.0, 'dy': 10.0}
|
||||
static const String windowPosition = 'windowPosition';
|
||||
|
||||
/// The key for saving the window status
|
||||
///
|
||||
/// The value is a json string with the following format:
|
||||
/// { 'windowMaximized': true }
|
||||
///
|
||||
static const String windowMaximized = 'windowMaximized';
|
||||
|
||||
static const String kDocumentAppearanceFontSize =
|
||||
'kDocumentAppearanceFontSize';
|
||||
static const String kDocumentAppearanceFontFamily =
|
||||
@@ -65,9 +72,40 @@ class KVKeys {
|
||||
/// {'feature_flag_1': true, 'feature_flag_2': false}
|
||||
static const String featureFlag = 'featureFlag';
|
||||
|
||||
/// The key for saving show notification icon option
|
||||
///
|
||||
/// The value is a boolean string
|
||||
static const String showNotificationIcon = 'showNotificationIcon';
|
||||
|
||||
/// The key for saving the last opened workspace id
|
||||
///
|
||||
/// The workspace id is a string.
|
||||
@Deprecated('deprecated in version 0.5.5')
|
||||
static const String lastOpenedWorkspaceId = 'lastOpenedWorkspaceId';
|
||||
|
||||
/// The key for saving the scale factor
|
||||
///
|
||||
/// The value is a double string.
|
||||
static const String scaleFactor = 'scaleFactor';
|
||||
|
||||
/// The key for saving the last opened tab (favorite, recent, space etc.)
|
||||
///
|
||||
/// The value is a int string.
|
||||
static const String lastOpenedSpace = 'lastOpenedSpace';
|
||||
|
||||
/// The key for saving the space tab order
|
||||
///
|
||||
/// The value is a json string with the following format:
|
||||
/// [0, 1, 2]
|
||||
static const String spaceOrder = 'spaceOrder';
|
||||
|
||||
/// The key for saving the last opened space id (space A, space B)
|
||||
///
|
||||
/// The value is a string.
|
||||
static const String lastOpenedSpaceId = 'lastOpenedSpaceId';
|
||||
|
||||
/// The key for saving the upgrade space tag
|
||||
///
|
||||
/// The value is a boolean string
|
||||
static const String hasUpgradedSpace = 'hasUpgradedSpace060';
|
||||
}
|
||||
@@ -1,6 +1,15 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/shared/window_title_bar.dart';
|
||||
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class CocoaWindowChannel {
|
||||
CocoaWindowChannel._();
|
||||
@@ -26,9 +35,14 @@ class CocoaWindowChannel {
|
||||
}
|
||||
|
||||
class MoveWindowDetector extends StatefulWidget {
|
||||
const MoveWindowDetector({super.key, this.child});
|
||||
const MoveWindowDetector({
|
||||
super.key,
|
||||
this.child,
|
||||
this.showTitleBar = false,
|
||||
});
|
||||
|
||||
final Widget? child;
|
||||
final bool showTitleBar;
|
||||
|
||||
@override
|
||||
MoveWindowDetectorState createState() => MoveWindowDetectorState();
|
||||
@@ -40,15 +54,32 @@ class MoveWindowDetectorState extends State<MoveWindowDetector> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!Platform.isMacOS) {
|
||||
if (!Platform.isMacOS && !Platform.isWindows) {
|
||||
return widget.child ?? const SizedBox.shrink();
|
||||
}
|
||||
|
||||
if (Platform.isWindows) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (widget.showTitleBar) ...[
|
||||
WindowTitleBar(
|
||||
leftChildren: [
|
||||
_buildToggleMenuButton(context),
|
||||
],
|
||||
),
|
||||
] else ...[
|
||||
const SizedBox(height: 5),
|
||||
],
|
||||
widget.child ?? const SizedBox.shrink(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
// https://stackoverflow.com/questions/52965799/flutter-gesturedetector-not-working-with-containers-in-stack
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onDoubleTap: () async {
|
||||
await CocoaWindowChannel.instance.zoom();
|
||||
},
|
||||
onDoubleTap: () async => CocoaWindowChannel.instance.zoom(),
|
||||
onPanStart: (DragStartDetails details) {
|
||||
winX = details.globalPosition.dx;
|
||||
winY = details.globalPosition.dy;
|
||||
@@ -65,4 +96,45 @@ class MoveWindowDetectorState extends State<MoveWindowDetector> {
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildToggleMenuButton(BuildContext context) {
|
||||
if (!context.read<HomeSettingBloc>().state.isMenuCollapsed) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
final textSpan = TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '${LocaleKeys.sideBar_openSidebar.tr()}\n',
|
||||
style: context.tooltipTextStyle(),
|
||||
),
|
||||
TextSpan(
|
||||
text: Platform.isMacOS ? '⌘+.' : 'Ctrl+\\',
|
||||
style: context
|
||||
.tooltipTextStyle()
|
||||
?.copyWith(color: Theme.of(context).hintColor),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return FlowyTooltip(
|
||||
richMessage: textSpan,
|
||||
child: Listener(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onPointerDown: (_) => context
|
||||
.read<HomeSettingBloc>()
|
||||
.add(const HomeSettingEvent.collapseMenu()),
|
||||
child: FlowyHover(
|
||||
child: Container(
|
||||
width: 24,
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: const RotatedBox(
|
||||
quarterTurns: 2,
|
||||
child: FlowySvg(FlowySvgs.hide_menu_s),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:string_validator/string_validator.dart';
|
||||
import 'package:url_launcher/url_launcher.dart' as launcher;
|
||||
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/notification/notification_helper.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document/notification.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
|
||||
// This value should be the same as the DOCUMENT_OBSERVABLE_SOURCE value
|
||||
const String _source = 'Document';
|
||||
|
||||
typedef DocumentNotificationCallback = void Function(
|
||||
DocumentNotification,
|
||||
FlowyResult<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class DocumentNotificationParser
|
||||
extends NotificationParser<DocumentNotification, FlowyError> {
|
||||
DocumentNotificationParser({
|
||||
|
||||
@@ -12,12 +12,6 @@ import 'notification_helper.dart';
|
||||
// This value should be the same as the FOLDER_OBSERVABLE_SOURCE value
|
||||
const String _source = 'Workspace';
|
||||
|
||||
// Folder
|
||||
typedef FolderNotificationCallback = void Function(
|
||||
FolderNotification,
|
||||
FlowyResult<Uint8List, FlowyError>,
|
||||
);
|
||||
|
||||
class FolderNotificationParser
|
||||
extends NotificationParser<FolderNotification, FlowyError> {
|
||||
FolderNotificationParser({
|
||||
|
||||