Compare commits
139
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c404ce5297 | ||
|
|
64dfad284e | ||
|
|
ea34b226f1 | ||
|
|
3b696e4d27 | ||
|
|
fd9b01ca27 | ||
|
|
1a82f3fff1 | ||
|
|
8e6f051dec | ||
|
|
8ceff03f3e | ||
|
|
e1bbcc415f | ||
|
|
567ba7d962 | ||
|
|
153416604d | ||
|
|
caa882dc37 | ||
|
|
9c05788834 | ||
|
|
6b585ef9a7 | ||
|
|
469ddacfcc | ||
|
|
e714548dc3 | ||
|
|
4d4e3bfb8a | ||
|
|
1ba339fabe | ||
|
|
257a7530a0 | ||
|
|
02126a1e24 | ||
|
|
97913c390b | ||
|
|
c1cf58b99e | ||
|
|
574b0ce84b | ||
|
|
90cf10e2c4 | ||
|
|
26ac896bac | ||
|
|
b36babf754 | ||
|
|
0d69b895aa | ||
|
|
3b48ca0f4b | ||
|
|
2e6ce16b11 | ||
|
|
edeec49121 | ||
|
|
5de716b19d | ||
|
|
cb2b933a90 | ||
|
|
37f99988eb | ||
|
|
99962dc3c6 | ||
|
|
bec1668272 | ||
|
|
063d54dfae | ||
|
|
59f79be290 | ||
|
|
813c8e6b86 | ||
|
|
23e3650570 | ||
|
|
092aefdd71 | ||
|
|
19361e2c68 | ||
|
|
6e48f02d25 | ||
|
|
46e45c3715 | ||
|
|
eca495ce63 | ||
|
|
70e72d843e | ||
|
|
7ac3b72a4f | ||
|
|
cc9072e9cc | ||
|
|
a0ee47b809 | ||
|
|
af40ff8eb1 | ||
|
|
b0211be03c | ||
|
|
3174ffed5c | ||
|
|
99c7252a15 | ||
|
|
99c81fed56 | ||
|
|
a01fc5b572 | ||
|
|
1b0bb1d5ff | ||
|
|
1ffd653515 | ||
|
|
e1c60084d3 | ||
|
|
4c6b6ffdac | ||
|
|
783d458100 | ||
|
|
9b7d38bca7 | ||
|
|
a8a8502577 | ||
|
|
6a1619d300 | ||
|
|
9b811f0166 | ||
|
|
7e1b77f872 | ||
|
|
b7c598ea56 | ||
|
|
da7c993fd6 | ||
|
|
c6dd5db6da | ||
|
|
e125e9493a | ||
|
|
5fbd07e96a | ||
|
|
0a6bee6232 | ||
|
|
552f2a73de | ||
|
|
eeb6b69f58 | ||
|
|
99889b9950 | ||
|
|
c51b495544 | ||
|
|
0d2841227a | ||
|
|
2dc7dc1a26 | ||
|
|
f0a494b41d | ||
|
|
806fc75a8a | ||
|
|
d3362bf96a | ||
|
|
7bef698f03 | ||
|
|
bea4b60a7a | ||
|
|
1100c8b7e1 | ||
|
|
728c75ff87 | ||
|
|
e01a59c772 | ||
|
|
164933325c | ||
|
|
88df232139 | ||
|
|
7d3159c8da | ||
|
|
e36f9aee4e | ||
|
|
0a5f98014c | ||
|
|
e5cb16f5b3 | ||
|
|
aa06c78b68 | ||
|
|
da03f40d29 | ||
|
|
6ddbb418eb | ||
|
|
f9f5ae04d0 | ||
|
|
71e7f54367 | ||
|
|
c5afbb97a0 | ||
|
|
d189c1ca4e | ||
|
|
630fdb8995 | ||
|
|
522143cfd8 | ||
|
|
1dedb909e5 | ||
|
|
605a079927 | ||
|
|
938abb396e | ||
|
|
a406dfbc43 | ||
|
|
f303674737 | ||
|
|
2bc9753f60 | ||
|
|
56cc7bb972 | ||
|
|
710e894446 | ||
|
|
d67d77f442 | ||
|
|
129db6925c | ||
|
|
924df56d28 | ||
|
|
c2b194b1d2 | ||
|
|
68986bd170 | ||
|
|
e4029d05dc | ||
|
|
f1bc9f860c | ||
|
|
b865037728 | ||
|
|
7e0de1f69c | ||
|
|
12c575dbbe | ||
|
|
f44fb02e4a | ||
|
|
4df59ca421 | ||
|
|
7fee00ab72 | ||
|
|
7271e0a108 | ||
|
|
877236baf9 | ||
|
|
2c66634c89 | ||
|
|
609ea27c42 | ||
|
|
aafdb80455 | ||
|
|
4888d5d528 | ||
|
|
21cc130e4e | ||
|
|
a02de1a435 | ||
|
|
477683d454 | ||
|
|
340f37e00d | ||
|
|
eea3004a97 | ||
|
|
f59772756a | ||
|
|
1cf4f493db | ||
|
|
c5a9dea721 | ||
|
|
7d2719dd71 | ||
|
|
2ef896fb4a | ||
|
|
a0c6fb2fe8 | ||
|
|
ee3054b355 | ||
|
|
bfe6d25322 |
No files matched your search
@@ -58,19 +58,24 @@ runs:
|
||||
|
||||
- name: Install prerequisites
|
||||
working-directory: frontend
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
||||
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
|
||||
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev libmpv-dev mpv
|
||||
elif [ "$RUNNER_OS" == "Windows" ]; then
|
||||
vcpkg integrate install
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
echo 'do nothing'
|
||||
fi
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
shell: bash
|
||||
run: |
|
||||
case $RUNNER_OS in
|
||||
Linux)
|
||||
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
|
||||
;;
|
||||
Windows)
|
||||
vcpkg integrate install
|
||||
vcpkg update
|
||||
;;
|
||||
macOS)
|
||||
# No additional prerequisites needed for macOS
|
||||
;;
|
||||
esac
|
||||
cargo make appflowy-flutter-deps-tools
|
||||
|
||||
- name: Build AppFlowy
|
||||
working-directory: frontend
|
||||
@@ -94,4 +99,4 @@ runs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ github.run_id }}-${{ matrix.os }}
|
||||
path: appflowy_flutter.tar.gz
|
||||
path: appflowy_flutter.tar.gz
|
||||
@@ -2,18 +2,10 @@ name: Docker-CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release/*
|
||||
paths:
|
||||
- frontend/**
|
||||
branches: ["main", "release/*"]
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- release/*
|
||||
paths:
|
||||
- frontend/**
|
||||
types: [ opened, synchronize, reopened, unlocked, ready_for_review ]
|
||||
branches: ["main", "release/*"]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -27,25 +19,29 @@ jobs:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Compose
|
||||
run: |
|
||||
docker-compose --version || {
|
||||
echo "Docker Compose not found, installing..."
|
||||
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||||
sudo chmod +x /usr/local/bin/docker-compose
|
||||
docker-compose --version
|
||||
}
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# cache the docker layers
|
||||
# don't cache anything temporarly, because it always triggers "no space left on device" error
|
||||
# - name: Cache Docker layers
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: /tmp/.buildx-cache
|
||||
# key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
# restore-keys: |
|
||||
# ${{ runner.os }}-buildx-
|
||||
|
||||
- name: Build the app
|
||||
shell: bash
|
||||
run: |
|
||||
set -eu -o pipefail
|
||||
cd frontend/scripts/docker-buildfiles
|
||||
docker-compose build --no-cache --progress=plain \
|
||||
| while read line; do \
|
||||
if [[ "$line" =~ ^Step[[:space:]] ]]; then \
|
||||
echo "$(date -u '+%H:%M:%S') | $line"; \
|
||||
else \
|
||||
echo "$line"; \
|
||||
fi; \
|
||||
done
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./frontend/scripts/docker-buildfiles/Dockerfile
|
||||
push: false
|
||||
# cache-from: type=local,src=/tmp/.buildx-cache
|
||||
# cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
||||
|
||||
# - name: Move cache
|
||||
# run: |
|
||||
# rm -rf /tmp/.buildx-cache
|
||||
# mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
||||
@@ -25,9 +25,9 @@ on:
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
FLUTTER_VERSION: "3.22.0"
|
||||
RUST_TOOLCHAIN: "1.77.2"
|
||||
CARGO_MAKE_VERSION: "0.36.6"
|
||||
FLUTTER_VERSION: "3.22.2"
|
||||
RUST_TOOLCHAIN: "1.80.1"
|
||||
CARGO_MAKE_VERSION: "0.37.18"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -73,9 +73,9 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ windows-latest ]
|
||||
os: [ windows-2019 ]
|
||||
include:
|
||||
- os: windows-latest
|
||||
- os: windows-2019
|
||||
flutter_profile: development-windows-x86
|
||||
target: x86_64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -241,12 +241,15 @@ jobs:
|
||||
cp deploy.env .env
|
||||
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
|
||||
sed -i 's/GOTRUE_EXTERNAL_GOOGLE_ENABLED=.*/GOTRUE_EXTERNAL_GOOGLE_ENABLED=true/' .env
|
||||
sed -i 's|GOTRUE_MAILER_AUTOCONFIRM=.*|GOTRUE_MAILER_AUTOCONFIRM=true|' .env
|
||||
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
|
||||
- name: Run Docker-Compose
|
||||
working-directory: AppFlowy-Cloud
|
||||
env:
|
||||
APPFLOWY_CLOUD_VERSION: 0.6.4-amd64
|
||||
APPFLOWY_CLOUD_VERSION: 0.6.29-amd64
|
||||
APPFLOWY_HISTORY_VERSION: 0.6.29-amd64
|
||||
APPFLOWY_WORKER_VERSION: 0.6.29-amd64
|
||||
run: |
|
||||
container_id=$(docker ps --filter name=appflowy-cloud-appflowy_cloud-1 -q)
|
||||
if [ -z "$container_id" ]; then
|
||||
@@ -317,7 +320,7 @@ jobs:
|
||||
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
|
||||
sudo apt-get install network-manager
|
||||
docker ps -a
|
||||
flutter test integration_test/cloud/cloud_runner.dart -d Linux --coverage
|
||||
flutter test integration_test/desktop/cloud/cloud_runner.dart -d Linux --coverage
|
||||
shell: bash
|
||||
|
||||
# split the integration tests into different machines to minimize the time
|
||||
@@ -330,7 +333,7 @@ jobs:
|
||||
os: [ ubuntu-latest ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
target: "x86_64-unknown-linux-gnu"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
@@ -360,7 +363,7 @@ jobs:
|
||||
os: [ ubuntu-latest ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
target: "x86_64-unknown-linux-gnu"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
@@ -390,7 +393,7 @@ jobs:
|
||||
os: [ ubuntu-latest ]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
target: "x86_64-unknown-linux-gnu"
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
@@ -409,4 +412,4 @@ jobs:
|
||||
flutter_version: ${{ env.FLUTTER_VERSION }}
|
||||
rust_toolchain: ${{ env.RUST_TOOLCHAIN }}
|
||||
cargo_make_version: ${{ env.CARGO_MAKE_VERSION }}
|
||||
rust_target: ${{ matrix.target }}
|
||||
rust_target: ${{ matrix.target }}
|
||||
@@ -232,10 +232,10 @@ jobs:
|
||||
matrix:
|
||||
job:
|
||||
- {
|
||||
targets: "aarch64-apple-darwin,x86_64-apple-darwin",
|
||||
os: macos-latest,
|
||||
extra-build-args: "",
|
||||
}
|
||||
targets: "aarch64-apple-darwin,x86_64-apple-darwin",
|
||||
os: macos-latest,
|
||||
extra-build-args: "",
|
||||
}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
@@ -336,12 +336,12 @@ jobs:
|
||||
matrix:
|
||||
job:
|
||||
- {
|
||||
arch: x86_64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
os: ubuntu-20.04,
|
||||
extra-build-args: "",
|
||||
flutter_profile: production-linux-x86_64,
|
||||
}
|
||||
arch: x86_64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
os: ubuntu-20.04,
|
||||
extra-build-args: "",
|
||||
flutter_profile: production-linux-x86_64,
|
||||
}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@@ -47,6 +47,8 @@ jobs:
|
||||
working-directory: AppFlowy-Cloud
|
||||
env:
|
||||
APPFLOWY_CLOUD_VERSION: 0.6.4-amd64
|
||||
APPFLOWY_HISTORY_VERSION: 0.6.29-amd64
|
||||
APPFLOWY_WORKER_VERSION: 0.6.29-amd64
|
||||
run: |
|
||||
container_id=$(docker ps --filter name=appflowy-cloud-appflowy_cloud-1 -q)
|
||||
if [ -z "$container_id" ]; then
|
||||
@@ -127,12 +129,15 @@ jobs:
|
||||
run: |
|
||||
cp deploy.env .env
|
||||
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
|
||||
sed -i 's|GOTRUE_MAILER_AUTOCONFIRM=.*|GOTRUE_MAILER_AUTOCONFIRM=true|' .env
|
||||
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
|
||||
|
||||
- name: Ensure AppFlowy-Cloud is Running with Correct Version
|
||||
working-directory: AppFlowy-Cloud
|
||||
env:
|
||||
APPFLOWY_CLOUD_VERSION: 0.6.4-amd64
|
||||
APPFLOWY_HISTORY_VERSION: 0.6.29-amd64
|
||||
APPFLOWY_WORKER_VERSION: 0.6.29-amd64
|
||||
run: |
|
||||
container_id=$(docker ps --filter name=appflowy-cloud-appflowy_cloud-1 -q)
|
||||
if [ -z "$container_id" ]; then
|
||||
|
||||
@@ -1,4 +1,30 @@
|
||||
# Release Notes
|
||||
## Version 0.7.0 - 19/09/2024
|
||||
### New Features
|
||||
- Support reordering blocks in document with drag and drop
|
||||
- Support for adding a cover to a row/card in databases
|
||||
- Added support for accessing settings on the sign-in page
|
||||
- Added "Move to" option to the document menu in top right corner
|
||||
- Support for adjusting the document width from settings
|
||||
- Show full name of a group on hover
|
||||
- Colored group names in kanban boards
|
||||
- Support "Ask AI" on multiple lines of text
|
||||
- Support for keyboard gestures to move cursor on Mobile
|
||||
- Added markdown support for quickly inserting a code block using three backticks
|
||||
|
||||
### Bug Fixes
|
||||
- Fixed a critical bug where the backtick character would crash the application
|
||||
- Fixed an issue with signing-in from the settings dialog where the dialog would persist
|
||||
- Fixed a visual bug with icon alignment in primary cell of database rows
|
||||
- Fixed a bug with filters applied where new rows were inserted in wrong position
|
||||
- Fixed a bug where "Untitled" would override the name of the row
|
||||
- Fixed page title not updating after renaming from "More"-menu
|
||||
- Fixed File block breaking row detail document
|
||||
- Fixed issues with reordering rows with sorting rules applied
|
||||
- Improvements to the File & Media type in Database
|
||||
- Performance improvement in Grid view
|
||||
- Fixed filters sometimes not applying properly in databases
|
||||
|
||||
## Version 0.6.9 - 09/09/2024
|
||||
### New Features
|
||||
- Added a new property type, 'Files & media'
|
||||
|
||||
@@ -44,8 +44,8 @@ AppFlowy is the AI workspace where you achieve more without losing control of yo
|
||||
- [Download AppFlowy Desktop (macOS, Windows, and Linux)](https://github.com/AppFlowy-IO/AppFlowy/releases)
|
||||
- Other channels: [FlatHub](https://flathub.org/apps/io.appflowy.AppFlowy), [Snapcraft](https://snapcraft.io/appflowy), [Sourceforge](https://sourceforge.net/projects/appflowy/)
|
||||
- Available on
|
||||
- App Store: iPhone
|
||||
- Play Store: Android 10 or above; ARMv7 is not supported
|
||||
- [App Store](https://apps.apple.com/app/appflowy/id6457261352): iPhone
|
||||
- [Play Store](https://play.google.com/store/apps/details?id=io.appflowy.appflowy): Android 10 or above; ARMv7 is not supported
|
||||
- [Self-hosting AppFlowy](https://docs.appflowy.io/docs/guides/appflowy/self-hosting-appflowy)
|
||||
- [Source](https://docs.appflowy.io/docs/documentation/appflowy/from-source)
|
||||
|
||||
|
||||
@@ -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.6.9"
|
||||
APPFLOWY_VERSION = "0.7.1"
|
||||
FLUTTER_DESKTOP_FEATURES = "dart"
|
||||
PRODUCT_NAME = "AppFlowy"
|
||||
MACOSX_DEPLOYMENT_TARGET = "11.0"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
"{""id"":""RGmzka"",""name"":""Name"",""field_type"":0,""type_options"":{""0"":{""data"":""""}},""is_primary"":true}","{""id"":""oYoH-q"",""name"":""Time Slot"",""field_type"":2,""type_options"":{""0"":{""date_format"":3,""data"":"""",""time_format"":1,""timezone_id"":""""},""2"":{""date_format"":3,""time_format"":1,""timezone_id"":""""}},""is_primary"":false}","{""id"":""zVrp17"",""name"":""Amount"",""field_type"":1,""type_options"":{""1"":{""scale"":0,""format"":4,""name"":""Number"",""symbol"":""RUB""},""0"":{""data"":"""",""symbol"":""RUB"",""name"":""Number"",""format"":0,""scale"":0}},""is_primary"":false}","{""id"":""_p4EGt"",""name"":""Delta"",""field_type"":1,""type_options"":{""1"":{""name"":""Number"",""format"":36,""symbol"":""RUB"",""scale"":0},""0"":{""data"":"""",""symbol"":""RUB"",""name"":""Number"",""format"":0,""scale"":0}},""is_primary"":false}","{""id"":""Z909lc"",""name"":""Email"",""field_type"":6,""type_options"":{""6"":{""url"":"""",""content"":""""},""0"":{""data"":"""",""content"":"""",""url"":""""}},""is_primary"":false}","{""id"":""dBrSc7"",""name"":""Registration Complete"",""field_type"":5,""type_options"":{""5"":{}},""is_primary"":false}","{""id"":""VoigvK"",""name"":""Progress"",""field_type"":7,""type_options"":{""0"":{""data"":""""},""7"":{}},""is_primary"":false}","{""id"":""gbbQwh"",""name"":""Attachments"",""field_type"":14,""type_options"":{""0"":{""data"":"""",""content"":""{\""files\"":[]}""},""14"":{""content"":""{\""files\"":[]}""}},""is_primary"":false}","{""id"":""id3L0G"",""name"":""Priority"",""field_type"":3,""type_options"":{""3"":{""content"":""{\""options\"":[{\""id\"":\""cplL\"",\""name\"":\""VIP\"",\""color\"":\""Purple\""},{\""id\"":\""GSf_\"",\""name\"":\""High\"",\""color\"":\""Blue\""},{\""id\"":\""qnja\"",\""name\"":\""Medium\"",\""color\"":\""Green\""}],\""disable_color\"":false}""}},""is_primary"":false}","{""id"":""541SFC"",""name"":""Tags"",""field_type"":4,""type_options"":{""0"":{""data"":"""",""content"":""{\""options\"":[],\""disable_color\"":false}""},""4"":{""content"":""{\""options\"":[{\""id\"":\""1i4f\"",\""name\"":\""Education\"",\""color\"":\""Yellow\""},{\""id\"":\""yORP\"",\""name\"":\""Health\"",\""color\"":\""Orange\""},{\""id\"":\""SEUo\"",\""name\"":\""Hobby\"",\""color\"":\""LightPink\""},{\""id\"":\""uRAO\"",\""name\"":\""Family\"",\""color\"":\""Pink\""},{\""id\"":\""R9I7\"",\""name\"":\""Work\"",\""color\"":\""Purple\""}],\""disable_color\"":false}""}},""is_primary"":false}","{""id"":""lg0B7O"",""name"":""Last modified"",""field_type"":8,""type_options"":{""0"":{""time_format"":1,""field_type"":8,""date_format"":3,""data"":"""",""include_time"":true},""8"":{""date_format"":3,""field_type"":8,""time_format"":1,""include_time"":true}},""is_primary"":false}","{""id"":""5riGR7"",""name"":""Created at"",""field_type"":9,""type_options"":{""0"":{""field_type"":9,""include_time"":true,""date_format"":3,""time_format"":1,""data"":""""},""9"":{""include_time"":true,""field_type"":9,""date_format"":3,""time_format"":1}},""is_primary"":false}"
|
||||
"{""data"":""Olaf"",""created_at"":1726063289,""last_modified"":1726063289,""field_type"":0}","{""last_modified"":1726122374,""created_at"":1726110045,""reminder_id"":"""",""is_range"":true,""include_time"":true,""end_timestamp"":""1725415200"",""field_type"":2,""data"":""1725256800""}","{""field_type"":1,""data"":""55200"",""last_modified"":1726063592,""created_at"":1726063592}","{""last_modified"":1726062441,""created_at"":1726062441,""data"":""0.5"",""field_type"":1}","{""created_at"":1726063719,""last_modified"":1726063732,""data"":""doyouwannabuildasnowman@arendelle.gov"",""field_type"":6}",,"{""field_type"":7,""last_modified"":1726064207,""data"":""{\""options\"":[{\""id\"":\""oqXQ\"",\""name\"":\""find elsa\"",\""color\"":\""Purple\""},{\""id\"":\""eQwp\"",\""name\"":\""find anna\"",\""color\"":\""Purple\""},{\""id\"":\""5-B3\"",\""name\"":\""play in the summertime\"",\""color\"":\""Purple\""},{\""id\"":\""UBFn\"",\""name\"":\""get a personal flurry\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[\""oqXQ\"",\""eQwp\"",\""UBFn\""]}"",""created_at"":1726064129}",,"{""created_at"":1726065208,""data"":""cplL"",""last_modified"":1726065282,""field_type"":3}","{""field_type"":4,""data"":""1i4f"",""last_modified"":1726105102,""created_at"":1726105102}","{""field_type"":8,""data"":""1726122374""}","{""data"":""1726060476"",""field_type"":9}"
|
||||
"{""field_type"":0,""last_modified"":1726063323,""data"":""Beatrice"",""created_at"":1726063323}",,"{""last_modified"":1726063638,""data"":""828600"",""created_at"":1726063607,""field_type"":1}","{""field_type"":1,""created_at"":1726062488,""data"":""-2.25"",""last_modified"":1726062488}","{""last_modified"":1726063790,""data"":""btreee17@gmail.com"",""field_type"":6,""created_at"":1726063790}","{""created_at"":1726062718,""data"":""Yes"",""field_type"":5,""last_modified"":1726062724}","{""created_at"":1726064277,""data"":""{\""options\"":[{\""id\"":\""BDuH\"",\""name\"":\""get the leaf node\"",\""color\"":\""Purple\""},{\""id\"":\""GXAr\"",\""name\"":\""upgrade to b+\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[]}"",""field_type"":7,""last_modified"":1726064293}",,"{""data"":""GSf_"",""created_at"":1726065288,""last_modified"":1726065288,""field_type"":3}","{""created_at"":1726105110,""data"":""yORP,uRAO"",""last_modified"":1726105111,""field_type"":4}","{""data"":""1726105111"",""field_type"":8}","{""field_type"":9,""data"":""1726060476""}"
|
||||
"{""last_modified"":1726063355,""created_at"":1726063355,""field_type"":0,""data"":""Lancelot""}","{""data"":""1726468159"",""is_range"":true,""end_timestamp"":""1726727359"",""reminder_id"":"""",""include_time"":false,""field_type"":2,""created_at"":1726122403,""last_modified"":1726122559}","{""created_at"":1726063617,""last_modified"":1726063617,""data"":""22500"",""field_type"":1}","{""data"":""11.6"",""last_modified"":1726062504,""field_type"":1,""created_at"":1726062504}","{""field_type"":6,""data"":""sir.lancelot@gmail.com"",""last_modified"":1726063812,""created_at"":1726063812}","{""data"":""No"",""field_type"":5,""last_modified"":1726062724,""created_at"":1726062375}",,,"{""data"":""cplL"",""created_at"":1726065286,""last_modified"":1726065286,""field_type"":3}","{""last_modified"":1726105237,""data"":""SEUo"",""created_at"":1726105237,""field_type"":4}","{""field_type"":8,""data"":""1726122559""}","{""field_type"":9,""data"":""1726060476""}"
|
||||
"{""data"":""Scotty"",""last_modified"":1726063399,""created_at"":1726063399,""field_type"":0}","{""reminder_id"":"""",""last_modified"":1726122418,""include_time"":true,""data"":""1725868800"",""end_timestamp"":""1726646400"",""created_at"":1726122381,""field_type"":2,""is_range"":true}","{""created_at"":1726063650,""last_modified"":1726063650,""data"":""10900"",""field_type"":1}","{""data"":""0"",""created_at"":1726062581,""last_modified"":1726062581,""field_type"":1}","{""last_modified"":1726063835,""created_at"":1726063835,""field_type"":6,""data"":""scottylikestosing@outlook.com""}","{""data"":""Yes"",""field_type"":5,""created_at"":1726062718,""last_modified"":1726062718}","{""created_at"":1726064309,""data"":""{\""options\"":[{\""id\"":\""Cw0K\"",\""name\"":\""vocal warmup\"",\""color\"":\""Purple\""},{\""id\"":\""nYMo\"",\""name\"":\""mixed voice training\"",\""color\"":\""Purple\""},{\""id\"":\""i-OX\"",\""name\"":\""belting training\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[\""Cw0K\"",\""nYMo\"",\""i-OX\""]}"",""field_type"":7,""last_modified"":1726064325}","{""last_modified"":1726122911,""created_at"":1726122835,""data"":[""{\""id\"":\""746a741d-98f8-4cc6-b807-a82d2e78c221\"",\""name\"":\""googlelogo_color_272x92dp.png\"",\""url\"":\""https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png\"",\""upload_type\"":\""NetworkMedia\"",\""file_type\"":\""Image\""}"",""{\""id\"":\""cbbab3ee-32ab-4438-a909-3f69f935a8bd\"",\""name\"":\""tL_v571NdZ0.svg\"",\""url\"":\""https://static.xx.fbcdn.net/rsrc.php/y9/r/tL_v571NdZ0.svg\"",\""upload_type\"":\""NetworkMedia\"",\""file_type\"":\""Link\""}""],""field_type"":14}",,"{""data"":""SEUo,yORP"",""field_type"":4,""last_modified"":1726105123,""created_at"":1726105115}","{""data"":""1726122911"",""field_type"":8}","{""data"":""1726060539"",""field_type"":9}"
|
||||
"{""field_type"":0,""created_at"":1726063405,""last_modified"":1726063421,""data"":""""}",,,"{""last_modified"":1726062625,""field_type"":1,""data"":"""",""created_at"":1726062607}",,"{""data"":""No"",""last_modified"":1726062702,""created_at"":1726062393,""field_type"":5}",,,,,"{""data"":""1726063421"",""field_type"":8}","{""data"":""1726060539"",""field_type"":9}"
|
||||
"{""field_type"":0,""data"":""Thomas"",""last_modified"":1726063421,""created_at"":1726063421}","{""reminder_id"":"""",""field_type"":2,""data"":""1725627600"",""is_range"":false,""created_at"":1726122583,""last_modified"":1726122593,""end_timestamp"":"""",""include_time"":true}","{""last_modified"":1726063666,""field_type"":1,""data"":""465800"",""created_at"":1726063666}","{""last_modified"":1726062516,""field_type"":1,""created_at"":1726062516,""data"":""-0.03""}","{""field_type"":6,""last_modified"":1726063848,""created_at"":1726063848,""data"":""tfp3827@gmail.com""}","{""field_type"":5,""last_modified"":1726062725,""data"":""Yes"",""created_at"":1726062376}","{""created_at"":1726064344,""data"":""{\""options\"":[{\""id\"":\""D6X8\"",\""name\"":\""brainstorm\"",\""color\"":\""Purple\""},{\""id\"":\""XVN9\"",\""name\"":\""schedule\"",\""color\"":\""Purple\""},{\""id\"":\""nJx8\"",\""name\"":\""shoot\"",\""color\"":\""Purple\""},{\""id\"":\""7Mrm\"",\""name\"":\""edit\"",\""color\"":\""Purple\""},{\""id\"":\""o6vg\"",\""name\"":\""publish\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[\""D6X8\""]}"",""last_modified"":1726064379,""field_type"":7}",,"{""last_modified"":1726065298,""created_at"":1726065298,""field_type"":3,""data"":""GSf_""}","{""data"":""yORP,SEUo"",""field_type"":4,""last_modified"":1726105229,""created_at"":1726105229}","{""data"":""1726122593"",""field_type"":8}","{""field_type"":9,""data"":""1726060540""}"
|
||||
"{""data"":""Juan"",""last_modified"":1726063423,""created_at"":1726063423,""field_type"":0}","{""created_at"":1726122510,""reminder_id"":"""",""include_time"":false,""is_range"":true,""last_modified"":1726122515,""data"":""1725604115"",""end_timestamp"":""1725776915"",""field_type"":2}","{""field_type"":1,""created_at"":1726063677,""last_modified"":1726063677,""data"":""93100""}","{""field_type"":1,""data"":""4.86"",""created_at"":1726062597,""last_modified"":1726062597}",,"{""last_modified"":1726062377,""field_type"":5,""data"":""Yes"",""created_at"":1726062377}","{""last_modified"":1726064412,""field_type"":7,""data"":""{\""options\"":[{\""id\"":\""tTDq\"",\""name\"":\""complete onboarding\"",\""color\"":\""Purple\""},{\""id\"":\""E8Ds\"",\""name\"":\""contact support\"",\""color\"":\""Purple\""},{\""id\"":\""RoGN\"",\""name\"":\""get started\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[\""tTDq\"",\""E8Ds\""]}"",""created_at"":1726064396}",,"{""created_at"":1726065278,""field_type"":3,""data"":""qnja"",""last_modified"":1726065278}","{""data"":""R9I7,yORP,1i4f"",""field_type"":4,""created_at"":1726105126,""last_modified"":1726105127}","{""data"":""1726122515"",""field_type"":8}","{""data"":""1726060541"",""field_type"":9}"
|
||||
"{""data"":""Alex"",""created_at"":1726063432,""last_modified"":1726063432,""field_type"":0}","{""reminder_id"":"""",""data"":""1725292800"",""include_time"":true,""last_modified"":1726122448,""created_at"":1726122422,""is_range"":true,""end_timestamp"":""1725551940"",""field_type"":2}","{""field_type"":1,""last_modified"":1726063683,""created_at"":1726063683,""data"":""3560""}","{""created_at"":1726062561,""data"":""1.96"",""last_modified"":1726062561,""field_type"":1}","{""last_modified"":1726063952,""created_at"":1726063931,""data"":""al3x1343@protonmail.com"",""field_type"":6}","{""last_modified"":1726062375,""field_type"":5,""created_at"":1726062375,""data"":""Yes""}","{""data"":""{\""options\"":[{\""id\"":\""qNyr\"",\""name\"":\""finish reading book\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[]}"",""created_at"":1726064616,""last_modified"":1726064616,""field_type"":7}",,"{""data"":""qnja"",""created_at"":1726065272,""last_modified"":1726065272,""field_type"":3}","{""created_at"":1726105180,""last_modified"":1726105180,""field_type"":4,""data"":""R9I7,1i4f""}","{""field_type"":8,""data"":""1726122448""}","{""field_type"":9,""data"":""1726060541""}"
|
||||
"{""last_modified"":1726063478,""created_at"":1726063436,""field_type"":0,""data"":""Alexander""}",,"{""field_type"":1,""last_modified"":1726063691,""created_at"":1726063691,""data"":""2073""}","{""field_type"":1,""data"":""0.5"",""last_modified"":1726062577,""created_at"":1726062577}","{""last_modified"":1726063991,""field_type"":6,""created_at"":1726063991,""data"":""alexandernotthedra@gmail.com""}","{""field_type"":5,""last_modified"":1726062378,""created_at"":1726062377,""data"":""No""}",,,"{""created_at"":1726065291,""data"":""GSf_"",""last_modified"":1726065291,""field_type"":3}","{""last_modified"":1726105142,""created_at"":1726105133,""data"":""SEUo"",""field_type"":4}","{""field_type"":8,""data"":""1726105142""}","{""field_type"":9,""data"":""1726060542""}"
|
||||
"{""field_type"":0,""created_at"":1726063454,""last_modified"":1726063454,""data"":""George""}","{""created_at"":1726122467,""end_timestamp"":""1726468070"",""include_time"":false,""is_range"":true,""reminder_id"":"""",""field_type"":2,""data"":""1726295270"",""last_modified"":1726122470}",,,"{""field_type"":6,""data"":""george.aq@appflowy.io"",""last_modified"":1726064104,""created_at"":1726064016}","{""last_modified"":1726062376,""created_at"":1726062376,""field_type"":5,""data"":""Yes""}","{""data"":""{\""options\"":[{\""id\"":\""s_dQ\"",\""name\"":\""bug triage\"",\""color\"":\""Purple\""},{\""id\"":\""-Zfo\"",\""name\"":\""fix bugs\"",\""color\"":\""Purple\""},{\""id\"":\""wsDN\"",\""name\"":\""attend meetings\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[\""s_dQ\"",\""-Zfo\""]}"",""last_modified"":1726064468,""created_at"":1726064424,""field_type"":7}","{""data"":[""{\""id\"":\""8a77f84d-64e9-4e67-b902-fa23980459ec\"",\""name\"":\""BQdTmxpRI6f.png\"",\""url\"":\""https://static.cdninstagram.com/rsrc.php/v3/ym/r/BQdTmxpRI6f.png\"",\""upload_type\"":\""NetworkMedia\"",\""file_type\"":\""Image\""}""],""field_type"":14,""created_at"":1726122956,""last_modified"":1726122956}","{""field_type"":3,""data"":""qnja"",""created_at"":1726065313,""last_modified"":1726065313}","{""data"":""R9I7,yORP"",""field_type"":4,""last_modified"":1726105198,""created_at"":1726105187}","{""data"":""1726122956"",""field_type"":8}","{""data"":""1726060543"",""field_type"":9}"
|
||||
"{""field_type"":0,""last_modified"":1726063467,""data"":""Joanna"",""created_at"":1726063467}","{""include_time"":false,""end_timestamp"":""1727072893"",""is_range"":true,""last_modified"":1726122493,""created_at"":1726122483,""data"":""1726554493"",""field_type"":2,""reminder_id"":""""}","{""last_modified"":1726065463,""data"":""16470"",""field_type"":1,""created_at"":1726065463}","{""created_at"":1726062626,""field_type"":1,""last_modified"":1726062626,""data"":""-5.36""}","{""last_modified"":1726064069,""data"":""joannastrawberry29+hello@gmail.com"",""created_at"":1726064069,""field_type"":6}",,"{""field_type"":7,""created_at"":1726064444,""last_modified"":1726064460,""data"":""{\""options\"":[{\""id\"":\""ZxJz\"",\""name\"":\""post on Twitter\"",\""color\"":\""Purple\""},{\""id\"":\""upwi\"",\""name\"":\""watch Youtube videos\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[\""upwi\""]}""}",,"{""created_at"":1726065317,""last_modified"":1726065317,""field_type"":3,""data"":""qnja""}","{""field_type"":4,""last_modified"":1726105173,""data"":""uRAO,yORP"",""created_at"":1726105170}","{""data"":""1726122493"",""field_type"":8}","{""data"":""1726060545"",""field_type"":9}"
|
||||
"{""last_modified"":1726063457,""created_at"":1726063457,""data"":""George"",""field_type"":0}","{""include_time"":true,""reminder_id"":"""",""field_type"":2,""is_range"":true,""created_at"":1726122521,""end_timestamp"":""1725829200"",""data"":""1725822900"",""last_modified"":1726122535}","{""last_modified"":1726065493,""field_type"":1,""data"":""9500"",""created_at"":1726065493}","{""last_modified"":1726062680,""created_at"":1726062680,""field_type"":1,""data"":""1.7""}","{""data"":""plgeorgebball@gmail.com"",""field_type"":6,""last_modified"":1726064087,""created_at"":1726064036}",,"{""last_modified"":1726064513,""data"":""{\""options\"":[{\""id\"":\""zy0x\"",\""name\"":\""game vs celtics\"",\""color\"":\""Purple\""},{\""id\"":\""WJsv\"",\""name\"":\""training\"",\""color\"":\""Purple\""},{\""id\"":\""w-f8\"",\""name\"":\""game vs spurs\"",\""color\"":\""Purple\""},{\""id\"":\""p1VQ\"",\""name\"":\""game vs knicks\"",\""color\"":\""Purple\""},{\""id\"":\""VjUA\"",\""name\"":\""recovery\"",\""color\"":\""Purple\""},{\""id\"":\""sQ8X\"",\""name\"":\""don't get injured\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[]}"",""created_at"":1726064486,""field_type"":7}",,"{""field_type"":3,""last_modified"":1726065310,""data"":""qnja"",""created_at"":1726065310}","{""created_at"":1726105205,""field_type"":4,""last_modified"":1726105249,""data"":""R9I7,1i4f,yORP,SEUo""}","{""data"":""1726122535"",""field_type"":8}","{""field_type"":9,""data"":""1726060546""}"
|
||||
"{""data"":""Judy"",""created_at"":1726063475,""field_type"":0,""last_modified"":1726063487}","{""end_timestamp"":"""",""reminder_id"":"""",""data"":""1726640950"",""field_type"":2,""include_time"":false,""created_at"":1726122550,""last_modified"":1726122550,""is_range"":false}",,,"{""created_at"":1726063882,""field_type"":6,""last_modified"":1726064000,""data"":""judysmithjr@outlook.com""}","{""last_modified"":1726062712,""field_type"":5,""data"":""Yes"",""created_at"":1726062712}","{""created_at"":1726064549,""field_type"":7,""data"":""{\""options\"":[{\""id\"":\""j8cC\"",\""name\"":\""finish training\"",\""color\"":\""Purple\""},{\""id\"":\""SmSk\"",\""name\"":\""brainwash\"",\""color\"":\""Purple\""},{\""id\"":\""mnf5\"",\""name\"":\""welcome to ba sing se\"",\""color\"":\""Purple\""},{\""id\"":\""hcrj\"",\""name\"":\""don't mess up\"",\""color\"":\""Purple\""}],\""selected_option_ids\"":[\""j8cC\"",\""SmSk\"",\""mnf5\"",\""hcrj\""]}"",""last_modified"":1726064591}",,,"{""field_type"":4,""last_modified"":1726105152,""created_at"":1726105152,""data"":""R9I7""}","{""field_type"":8,""data"":""1726122550""}","{""field_type"":9,""data"":""1726060549""}"
|
||||
@@ -1,19 +0,0 @@
|
||||
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 'user_setting_sync_test.dart' as user_sync_test;
|
||||
import 'workspace/change_name_and_icon_test.dart'
|
||||
as change_workspace_name_and_icon_test;
|
||||
import 'workspace/collaborative_workspace_test.dart'
|
||||
as collaboration_workspace_test;
|
||||
|
||||
Future<void> main() async {
|
||||
preset_af_cloud_env_test.main();
|
||||
appflowy_cloud_auth_test.main();
|
||||
user_sync_test.main();
|
||||
anon_user_continue_test.main();
|
||||
|
||||
// workspace
|
||||
collaboration_workspace_test.main();
|
||||
change_workspace_name_and_icon_test.main();
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
// import 'package:appflowy/env/cloud_env.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:flutter_test/flutter_test.dart';
|
||||
// import 'package:integration_test/integration_test.dart';
|
||||
|
||||
// import '../shared/util.dart';
|
||||
|
||||
// void main() {
|
||||
// IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// group('supabase auth', () {
|
||||
// testWidgets('sign in with supabase', (tester) async {
|
||||
// await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
// await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
// });
|
||||
|
||||
// testWidgets('sign out with supabase', (tester) async {
|
||||
// await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
|
||||
// // Open the setting page and sign out
|
||||
// await tester.openSettings();
|
||||
// await tester.openSettingsPage(SettingsPage.account);
|
||||
// await tester.logout();
|
||||
|
||||
// // Go to the sign in page again
|
||||
// await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
// tester.expectToSeeGoogleLoginButton();
|
||||
// });
|
||||
|
||||
// testWidgets('sign in as anonymous', (tester) async {
|
||||
// await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
// await tester.tapSignInAsGuest();
|
||||
|
||||
// // should not see the sync setting page when sign in as anonymous
|
||||
// await tester.openSettings();
|
||||
// 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();
|
||||
// });
|
||||
|
||||
// // testWidgets('enable encryption', (tester) async {
|
||||
// // await tester.initializeAppFlowy(cloudType: CloudType.supabase);
|
||||
// // await tester.tapGoogleLoginInButton();
|
||||
|
||||
// // // Open the setting page and sign out
|
||||
// // await tester.openSettings();
|
||||
// // await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// // // the switch should be off by default
|
||||
// // tester.assertEnableEncryptSwitchValue(false);
|
||||
// // await tester.toggleEnableEncrypt();
|
||||
|
||||
// // // the switch should be on after toggling
|
||||
// // tester.assertEnableEncryptSwitchValue(true);
|
||||
|
||||
// // // the switch can not be toggled back to off
|
||||
// // await tester.toggleEnableEncrypt();
|
||||
// // tester.assertEnableEncryptSwitchValue(true);
|
||||
// // });
|
||||
|
||||
// testWidgets('enable sync', (tester) async {
|
||||
// await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
|
||||
// // Open the setting page and sign out
|
||||
// await tester.openSettings();
|
||||
// await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// // the switch should be on by default
|
||||
// tester.assertSupabaseEnableSyncSwitchValue(true);
|
||||
// await tester.toggleEnableSync(SupabaseEnableSync);
|
||||
|
||||
// // the switch should be off
|
||||
// tester.assertSupabaseEnableSyncSwitchValue(false);
|
||||
|
||||
// // the switch should be on after toggling
|
||||
// await tester.toggleEnableSync(SupabaseEnableSync);
|
||||
// tester.assertSupabaseEnableSyncSwitchValue(true);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.dart';
|
||||
import 'package:appflowy/shared/feature_flags.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/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/widgets/user_avatar.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../shared/database_test_op.dart';
|
||||
import '../../shared/dir.dart';
|
||||
import '../../shared/emoji.dart';
|
||||
import '../../shared/mock/mock_file_picker.dart';
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// 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;
|
||||
// }
|
||||
|
||||
// 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);
|
||||
|
||||
// final loading = find.byType(Loading);
|
||||
// await tester.pumpUntilNotFound(loading);
|
||||
|
||||
// Finder success;
|
||||
|
||||
// final Finder items = find.byType(WorkspaceMenuItem);
|
||||
|
||||
// // 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);
|
||||
// },
|
||||
// );
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -25,6 +25,7 @@ void main() {
|
||||
name: fieldName,
|
||||
layout: ViewLayoutPB.Board,
|
||||
);
|
||||
await tester.dismissRowDetailPage();
|
||||
await tester.tapButton(card1);
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
fieldName,
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart';
|
||||
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/cell_editor/select_option_cell_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/field/type_option_editor/select/select_option_editor.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/material.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 group test', () {
|
||||
group('board group test:', () {
|
||||
testWidgets('move row to another group', (tester) async {
|
||||
const card1Name = 'Card 1';
|
||||
await tester.initializeAppFlowy();
|
||||
@@ -46,5 +50,105 @@ void main() {
|
||||
final card1StatusText = tester.widget<Text>(card1StatusFinder).data;
|
||||
expect(card1StatusText, 'Doing');
|
||||
});
|
||||
|
||||
testWidgets('rename group', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
final headers = find.byType(BoardColumnHeader);
|
||||
expect(headers, findsNWidgets(4));
|
||||
|
||||
// try to tap no status
|
||||
final noStatus = headers.first;
|
||||
expect(
|
||||
find.descendant(of: noStatus, matching: find.text("No Status")),
|
||||
findsOneWidget,
|
||||
);
|
||||
await tester.tapButton(noStatus);
|
||||
expect(
|
||||
find.descendant(of: noStatus, matching: find.byType(TextField)),
|
||||
findsNothing,
|
||||
);
|
||||
|
||||
// tap on To Do and edit it
|
||||
final todo = headers.at(1);
|
||||
expect(
|
||||
find.descendant(of: todo, matching: find.text("To Do")),
|
||||
findsOneWidget,
|
||||
);
|
||||
await tester.tapButton(todo);
|
||||
await tester.enterText(
|
||||
find.descendant(of: todo, matching: find.byType(TextField)),
|
||||
"tada",
|
||||
);
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final newHeaders = find.byType(BoardColumnHeader);
|
||||
expect(newHeaders, findsNWidgets(4));
|
||||
final tada = find.byType(BoardColumnHeader).at(1);
|
||||
expect(
|
||||
find.descendant(of: tada, matching: find.byType(TextField)),
|
||||
findsNothing,
|
||||
);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: tada,
|
||||
matching: find.text("tada"),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('edit select option from row detail', (tester) async {
|
||||
const card1Name = 'Card 1';
|
||||
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
await tester.tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(RowCard),
|
||||
matching: find.text(card1Name),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tapGridFieldWithNameInRowDetailPage("Status");
|
||||
await tester.tapButton(
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is SelectOptionTagCell && widget.option.name == "To Do",
|
||||
),
|
||||
);
|
||||
final editor = find.byType(SelectOptionEditor);
|
||||
await tester.enterText(
|
||||
find.descendant(of: editor, matching: find.byType(TextField)),
|
||||
"tada",
|
||||
);
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.dismissFieldEditor();
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
final newHeaders = find.byType(BoardColumnHeader);
|
||||
expect(newHeaders, findsNWidgets(4));
|
||||
final tada = find.byType(BoardColumnHeader).at(1);
|
||||
expect(
|
||||
find.descendant(of: tada, matching: find.byType(TextField)),
|
||||
findsNothing,
|
||||
);
|
||||
expect(
|
||||
find.descendant(
|
||||
of: tada,
|
||||
matching: find.text("tada"),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+36
-35
@@ -1,13 +1,14 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/widgets/board_hidden_groups.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.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 '../../shared/database_test_op.dart';
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
@@ -77,47 +78,47 @@ void main() {
|
||||
shownGroups = tester.widgetList(find.byType(BoardColumnHeader)).length;
|
||||
expect(shownGroups, 4);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('delete a group', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
testWidgets('delete a group', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
|
||||
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 4);
|
||||
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 4);
|
||||
|
||||
// tap group option button for the first group. Delete shouldn't show up
|
||||
await tester.tapButton(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byFlowySvg(FlowySvgs.details_horizontal_s),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
expect(find.byFlowySvg(FlowySvgs.delete_s), findsNothing);
|
||||
// tap group option button for the first group. Delete shouldn't show up
|
||||
await tester.tapButton(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byFlowySvg(FlowySvgs.details_horizontal_s),
|
||||
)
|
||||
.first,
|
||||
);
|
||||
expect(find.byFlowySvg(FlowySvgs.delete_s), findsNothing);
|
||||
|
||||
// dismiss the popup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
await tester.pumpAndSettle();
|
||||
// dismiss the popup
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// tap group option button for the first group. Delete should show up
|
||||
await tester.tapButton(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byFlowySvg(FlowySvgs.details_horizontal_s),
|
||||
)
|
||||
.at(1),
|
||||
);
|
||||
expect(find.byFlowySvg(FlowySvgs.delete_s), findsOneWidget);
|
||||
// tap group option button for the first group. Delete should show up
|
||||
await tester.tapButton(
|
||||
find
|
||||
.descendant(
|
||||
of: find.byType(BoardColumnHeader),
|
||||
matching: find.byFlowySvg(FlowySvgs.details_horizontal_s),
|
||||
)
|
||||
.at(1),
|
||||
);
|
||||
expect(find.byFlowySvg(FlowySvgs.delete_s), findsOneWidget);
|
||||
|
||||
// Tap the delete button and confirm
|
||||
await tester.tapButton(find.byFlowySvg(FlowySvgs.delete_s));
|
||||
await tester.tapDialogOkButton();
|
||||
// Tap the delete button and confirm
|
||||
await tester.tapButton(find.byFlowySvg(FlowySvgs.delete_s));
|
||||
await tester.tapButtonWithName(LocaleKeys.space_delete.tr());
|
||||
|
||||
// Expect number of groups to decrease by one
|
||||
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 3);
|
||||
// Expect number of groups to decrease by one
|
||||
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 3);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ void main() {
|
||||
},
|
||||
);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
await tester.tapOKButton();
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
expect(find.text(name), findsNothing);
|
||||
});
|
||||
|
||||
@@ -51,6 +51,37 @@ void main() {
|
||||
expect(find.textContaining(name, findRichText: true), findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('duplicate item in ToDo card then delete', (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.hoverOnWidget(
|
||||
card1,
|
||||
onHover: () async {
|
||||
final moreOption = find.byType(MoreCardOptionsAccessory);
|
||||
await tester.tapButton(moreOption);
|
||||
},
|
||||
);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_duplicate.tr());
|
||||
expect(find.textContaining(name, findRichText: true), findsNWidgets(2));
|
||||
|
||||
// get the last widget that contains the name
|
||||
final duplicatedCard = find.textContaining(name, findRichText: true).last;
|
||||
await tester.hoverOnWidget(
|
||||
duplicatedCard,
|
||||
onHover: () async {
|
||||
final moreOption = find.byType(MoreCardOptionsAccessory);
|
||||
await tester.tapButton(moreOption);
|
||||
},
|
||||
);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
expect(find.textContaining(name, findRichText: true), findsNWidgets(1));
|
||||
});
|
||||
|
||||
testWidgets('add new group', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
@@ -3,6 +3,8 @@ import 'package:integration_test/integration_test.dart';
|
||||
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;
|
||||
import 'board_field_test.dart' as board_field_test;
|
||||
import 'board_hide_groups_test.dart' as board_hide_groups_test;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -11,4 +13,6 @@ void main() {
|
||||
board_row_test.main();
|
||||
board_add_row_test.main();
|
||||
board_group_test.main();
|
||||
board_field_test.main();
|
||||
board_hide_groups_test.main();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import 'document/document_test_runner.dart' as document_test_runner;
|
||||
import 'sidebar/sidebar_move_page_test.dart' as sidebar_move_page_test;
|
||||
import 'uncategorized/uncategorized_test_runner.dart'
|
||||
as uncategorized_test_runner;
|
||||
import 'workspace/workspace_test_runner.dart' as workspace_test_runner;
|
||||
import 'data_migration/data_migration_test_runner.dart' as data_migration_test_runner;
|
||||
|
||||
Future<void> main() async {
|
||||
data_migration_test_runner.main();
|
||||
// uncategorized
|
||||
uncategorized_test_runner.main();
|
||||
|
||||
// workspace
|
||||
workspace_test_runner.main();
|
||||
|
||||
// document
|
||||
document_test_runner.main();
|
||||
|
||||
// sidebar
|
||||
sidebar_move_page_test.main();
|
||||
|
||||
}
|
||||
+16
-28
@@ -24,24 +24,28 @@ 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';
|
||||
import '../../../shared/dir.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
import '../../board/board_hide_groups_test.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('appflowy cloud', () {
|
||||
testWidgets('anon user and then sign in', (tester) async {
|
||||
testWidgets('anon user -> sign in -> open imported space', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
|
||||
await tester.tapContinousAnotherWay();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
const pageName = 'Test Document';
|
||||
await tester.createNewPageWithNameUnderParent(name: pageName);
|
||||
tester.expectToSeePageName(pageName);
|
||||
|
||||
// rename the name of the anon user
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
@@ -60,33 +64,17 @@ void main() {
|
||||
|
||||
// sign up with Google
|
||||
await tester.tapGoogleLoginInButton();
|
||||
// await tester.pumpAndSettle(const Duration(seconds: 16));
|
||||
|
||||
// sign out
|
||||
// open the imported space
|
||||
await tester.expectToSeeHomePage();
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
await tester.clickSpaceHeader();
|
||||
|
||||
// Scroll to sign-out
|
||||
await tester.scrollUntilVisible(
|
||||
find.byType(AccountSignInOutButton),
|
||||
100,
|
||||
scrollable: find.findSettingsScrollable(),
|
||||
);
|
||||
// After import the anon user data, we will create a new space for it
|
||||
await tester.openSpace("Getting started");
|
||||
await tester.openPage(pageName);
|
||||
|
||||
await tester.logout();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// tap the continue as anonymous button
|
||||
await tester
|
||||
.tapButton(find.text(LocaleKeys.signIn_loginStartWithAnonymous.tr()));
|
||||
await tester.expectToSeeHomePage();
|
||||
|
||||
// New anon user name
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.account);
|
||||
final userNameInput =
|
||||
tester.widget(find.byType(AccountUserProfile)) as AccountUserProfile;
|
||||
expect(userNameInput.name, 'Me');
|
||||
});
|
||||
});
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
import 'anon_user_data_migration_test.dart' as anon_user_test;
|
||||
|
||||
void main() async {
|
||||
anon_user_test.main();
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../../shared/constants.dart';
|
||||
import '../../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('document option actions:', () {
|
||||
testWidgets('drag block to the top', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// open getting started page
|
||||
await tester.openPage(Constants.gettingStartedPageName);
|
||||
|
||||
// before move
|
||||
final beforeMoveBlock = tester.editor.getNodeAtPath([1]);
|
||||
|
||||
// move the desktop guide to the top, above the getting started
|
||||
await tester.editor.dragBlock(
|
||||
[1],
|
||||
const Offset(20, -80),
|
||||
);
|
||||
|
||||
// wait for the move animation to complete
|
||||
await tester.pumpAndSettle(Durations.short1);
|
||||
|
||||
// check if the block is moved to the top
|
||||
final afterMoveBlock = tester.editor.getNodeAtPath([0]);
|
||||
expect(afterMoveBlock.delta, beforeMoveBlock.delta);
|
||||
});
|
||||
|
||||
testWidgets('drag block to other block\'s child', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// open getting started page
|
||||
await tester.openPage(Constants.gettingStartedPageName);
|
||||
|
||||
// before move
|
||||
final beforeMoveBlock = tester.editor.getNodeAtPath([10]);
|
||||
|
||||
// move the checkbox to the child of the block at path [9]
|
||||
await tester.editor.dragBlock(
|
||||
[10],
|
||||
const Offset(80, -30),
|
||||
);
|
||||
|
||||
// wait for the move animation to complete
|
||||
await tester.pumpAndSettle(Durations.short1);
|
||||
|
||||
// check if the block is moved to the child of the block at path [9]
|
||||
final afterMoveBlock = tester.editor.getNodeAtPath([9, 0]);
|
||||
expect(afterMoveBlock.delta, beforeMoveBlock.delta);
|
||||
});
|
||||
|
||||
// Copy link to block feature is disable temporarily, enable this test when the feature is ready.
|
||||
// testWidgets('copy block link', (tester) async {
|
||||
// await tester.initializeAppFlowy(
|
||||
// cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
// );
|
||||
// await tester.tapGoogleLoginInButton();
|
||||
// await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// // open getting started page
|
||||
// await tester.openPage(Constants.gettingStartedPageName);
|
||||
|
||||
// // hover and click on the option menu button beside the block component.
|
||||
// await tester.editor.hoverAndClickOptionMenuButton([0]);
|
||||
|
||||
// // click the copy link to block option
|
||||
// await tester.tap(
|
||||
// find.findTextInFlowyText(
|
||||
// LocaleKeys.document_plugins_optionAction_copyLinkToBlock.tr(),
|
||||
// ),
|
||||
// );
|
||||
// await tester.pumpAndSettle(Durations.short1);
|
||||
|
||||
// // check the clipboard
|
||||
// final content = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
// expect(
|
||||
// content?.text,
|
||||
// matches(
|
||||
// r'^https:\/\/appflowy\.com\/app\/[a-f0-9-]{36}\/[a-f0-9-]{36}\?blockId=[A-Za-z0-9_-]+$',
|
||||
// ),
|
||||
// );
|
||||
// });
|
||||
|
||||
testWidgets('hover on the block and delete it', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// open getting started page
|
||||
await tester.openPage(Constants.gettingStartedPageName);
|
||||
|
||||
// before delete
|
||||
final path = [1];
|
||||
final beforeDeletedBlock = tester.editor.getNodeAtPath(path);
|
||||
|
||||
// hover on the block and delete it
|
||||
final optionButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is DraggableOptionButton &&
|
||||
widget.blockComponentContext.node.path.equals(path),
|
||||
);
|
||||
|
||||
await tester.hoverOnWidget(
|
||||
optionButton,
|
||||
onHover: () async {
|
||||
// click the delete button
|
||||
await tester.tapButton(optionButton);
|
||||
},
|
||||
);
|
||||
await tester.pumpAndSettle(Durations.short1);
|
||||
|
||||
// click the delete button
|
||||
final deleteButton =
|
||||
find.findTextInFlowyText(LocaleKeys.button_delete.tr());
|
||||
await tester.tapButton(deleteButton);
|
||||
|
||||
// wait for the deletion
|
||||
await tester.pumpAndSettle(Durations.short1);
|
||||
|
||||
// check if the block is deleted
|
||||
final afterDeletedBlock = tester.editor.getNodeAtPath([1]);
|
||||
expect(afterDeletedBlock.id, isNot(equals(beforeDeletedBlock.id)));
|
||||
});
|
||||
});
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'document_option_actions_test.dart' as document_option_actions_test;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
document_option_actions_test.main();
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.dart';
|
||||
import 'package:appflowy/shared/feature_flags.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/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/widgets/user_avatar.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
|
||||
import '../../../shared/constants.dart';
|
||||
import '../../../shared/database_test_op.dart';
|
||||
import '../../../shared/dir.dart';
|
||||
import '../../../shared/emoji.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('sidebar move page: ', () {
|
||||
testWidgets('create a new document and move it to Getting started',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
const pageName = 'Document';
|
||||
|
||||
await tester.createNewPageInSpace(
|
||||
spaceName: Constants.generalSpaceName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
pageName: pageName,
|
||||
);
|
||||
|
||||
// click the ... button and move to Getting started
|
||||
await tester.hoverOnPageName(
|
||||
pageName,
|
||||
onHover: () async {
|
||||
await tester.tapPageOptionButton();
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.disclosureAction_moveTo.tr(),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// expect to see two pages
|
||||
// one is in the sidebar, the other is in the move to page list
|
||||
// 1. Getting started
|
||||
// 2. To-dos
|
||||
final gettingStarted = find.findTextInFlowyText(
|
||||
Constants.gettingStartedPageName,
|
||||
);
|
||||
final toDos = find.findTextInFlowyText(Constants.toDosPageName);
|
||||
await tester.pumpUntilFound(gettingStarted);
|
||||
await tester.pumpUntilFound(toDos);
|
||||
expect(gettingStarted, findsNWidgets(2));
|
||||
|
||||
// skip the length check on Linux temporarily,
|
||||
// because it failed in expect check but the previous pumpUntilFound is successful
|
||||
if (!UniversalPlatform.isLinux) {
|
||||
expect(toDos, findsNWidgets(2));
|
||||
|
||||
// hover on the todos page, and will see a forbidden icon
|
||||
await tester.hoverOnWidget(
|
||||
toDos.last,
|
||||
onHover: () async {
|
||||
final tooltips = find.byTooltip(
|
||||
LocaleKeys.space_cannotMovePageToDatabase.tr(),
|
||||
);
|
||||
expect(tooltips, findsOneWidget);
|
||||
},
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
// move the current page to Getting started
|
||||
await tester.tapButton(
|
||||
gettingStarted.last,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// after moving, expect to not see the page name in the sidebar
|
||||
final page = tester.findPageName(pageName);
|
||||
expect(page, findsNothing);
|
||||
|
||||
// click to expand the getting started page
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: Constants.gettingStartedPageName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// expect to see the page name in the getting started page
|
||||
final pageInGettingStarted = tester.findPageName(
|
||||
pageName,
|
||||
parentName: Constants.gettingStartedPageName,
|
||||
);
|
||||
expect(pageInGettingStarted, findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
+6
-2
@@ -15,8 +15,8 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../shared/mock/mock_file_picker.dart';
|
||||
import '../shared/util.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -86,10 +86,13 @@ void main() {
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.cloud);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// the switch should be on by default
|
||||
tester.assertAppFlowyCloudEnableSyncSwitchValue(true);
|
||||
await tester.toggleEnableSync(AppFlowyCloudEnableSync);
|
||||
// wait for the switch animation
|
||||
await tester.wait(250);
|
||||
|
||||
// the switch should be off
|
||||
tester.assertAppFlowyCloudEnableSyncSwitchValue(false);
|
||||
@@ -97,6 +100,7 @@ void main() {
|
||||
// the switch should be on after toggling
|
||||
await tester.toggleEnableSync(AppFlowyCloudEnableSync);
|
||||
tester.assertAppFlowyCloudEnableSyncSwitchValue(true);
|
||||
await tester.wait(250);
|
||||
});
|
||||
});
|
||||
}
|
||||
+3
-3
@@ -17,9 +17,9 @@ 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';
|
||||
import '../../../shared/dir.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
+1
-1
@@ -2,7 +2,7 @@ import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../shared/util.dart';
|
||||
import '../../../shared/util.dart';
|
||||
|
||||
// This test is meaningless, just for preventing the CI from failing.
|
||||
void main() {
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test;
|
||||
import 'empty_test.dart' as preset_af_cloud_env_test;
|
||||
import 'user_setting_sync_test.dart' as user_sync_test;
|
||||
|
||||
void main() async {
|
||||
preset_af_cloud_env_test.main();
|
||||
appflowy_cloud_auth_test.main();
|
||||
user_sync_test.main();
|
||||
}
|
||||
+6
-6
@@ -22,12 +22,12 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
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';
|
||||
import '../shared/mock/mock_file_picker.dart';
|
||||
import '../shared/util.dart';
|
||||
import '../../../shared/database_test_op.dart';
|
||||
import '../../../shared/dir.dart';
|
||||
import '../../../shared/emoji.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
import '../../board/board_hide_groups_test.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
+3
-3
@@ -15,9 +15,9 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../shared/mock/mock_file_picker.dart';
|
||||
import '../../shared/util.dart';
|
||||
import '../../shared/workspace.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
import '../../../shared/workspace.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.dart';
|
||||
import 'package:appflowy/shared/feature_flags.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/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/widgets/user_avatar.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/uuid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../../shared/database_test_op.dart';
|
||||
import '../../../shared/dir.dart';
|
||||
import '../../../shared/emoji.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
const name = 'AppFlowy.IO';
|
||||
// the workspace will be opened after created
|
||||
await tester.createCollaborativeWorkspace(name);
|
||||
|
||||
final loading = find.byType(Loading);
|
||||
await tester.pumpUntilNotFound(loading);
|
||||
|
||||
Finder success;
|
||||
|
||||
final Finder items = find.byType(WorkspaceMenuItem);
|
||||
|
||||
// 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);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
|
||||
import 'package:appflowy/plugins/shared/share/constants.dart';
|
||||
import 'package:appflowy/plugins/shared/share/share_menu.dart';
|
||||
import 'package:appflowy/shared/feature_flags.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/home/menu/sidebar/workspace/_sidebar_workspace_icon.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_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import '../../../shared/constants.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
import '../../../shared/workspace.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Share menu:', () {
|
||||
testWidgets('share tab', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
const pageName = 'Document';
|
||||
|
||||
await tester.createNewPageInSpace(
|
||||
spaceName: Constants.generalSpaceName,
|
||||
layout: ViewLayoutPB.Document,
|
||||
pageName: pageName,
|
||||
);
|
||||
|
||||
// click the share button
|
||||
await tester.tapShareButton();
|
||||
|
||||
// expect the share menu is shown
|
||||
final shareMenu = find.byType(ShareMenu);
|
||||
expect(shareMenu, findsOneWidget);
|
||||
|
||||
// click the copy link button
|
||||
final copyLinkButton = find.textContaining(
|
||||
LocaleKeys.button_copyLink.tr(),
|
||||
);
|
||||
await tester.tapButton(copyLinkButton);
|
||||
|
||||
// read the clipboard content
|
||||
final clipboardContent = await getIt<ClipboardService>().getData();
|
||||
final plainText = clipboardContent.plainText;
|
||||
expect(
|
||||
plainText,
|
||||
startsWith(ShareConstants.shareBaseUrl),
|
||||
);
|
||||
|
||||
final shareValues = plainText!
|
||||
.replaceAll('${ShareConstants.shareBaseUrl}/', '')
|
||||
.split('/');
|
||||
final workspaceId = shareValues[0];
|
||||
expect(workspaceId, isNotEmpty);
|
||||
final pageId = shareValues[1];
|
||||
expect(pageId, isNotEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// ignore_for_file: unused_import
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/openai/widgets/loading.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
|
||||
import 'package:appflowy/shared/feature_flags.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/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/pages/settings_workspace_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/shared/setting_list_tile.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.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:flowy_infra_ui/flowy_infra_ui.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/database_test_op.dart';
|
||||
import '../../../shared/dir.dart';
|
||||
import '../../../shared/emoji.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('workspace settings: ', () {
|
||||
testWidgets(
|
||||
'change document width',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.workspace);
|
||||
|
||||
final documentWidthSettings = find.findTextInFlowyText(
|
||||
LocaleKeys.settings_appearance_documentSettings_width.tr(),
|
||||
);
|
||||
|
||||
final scrollable = find.ancestor(
|
||||
of: find.byType(SettingsWorkspaceView),
|
||||
matching: find.descendant(
|
||||
of: find.byType(SingleChildScrollView),
|
||||
matching: find.byType(Scrollable),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.scrollUntilVisible(
|
||||
documentWidthSettings,
|
||||
0,
|
||||
scrollable: scrollable,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// change the document width
|
||||
final slider = find.byType(Slider);
|
||||
final oldValue = tester.widget<Slider>(slider).value;
|
||||
await tester.drag(slider, const Offset(-100, 0));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// check the document width is changed
|
||||
expect(tester.widget<Slider>(slider).value, lessThan(oldValue));
|
||||
|
||||
// click the reset button
|
||||
final resetButton = find.descendant(
|
||||
of: find.byType(DocumentPaddingSetting),
|
||||
matching: find.byType(SettingsResetButton),
|
||||
);
|
||||
await tester.tap(resetButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// check the document width is reset
|
||||
expect(
|
||||
tester.widget<Slider>(slider).value,
|
||||
EditorStyleCustomizer.maxDocumentWidth,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'change_name_and_icon_test.dart' as change_name_and_icon_test;
|
||||
import 'collaborative_workspace_test.dart' as collaborative_workspace_test;
|
||||
import 'share_menu_test.dart' as share_menu_test;
|
||||
import 'workspace_settings_test.dart' as workspace_settings_test;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
workspace_settings_test.main();
|
||||
share_menu_test.main();
|
||||
collaborative_workspace_test.main();
|
||||
change_name_and_icon_test.main();
|
||||
}
|
||||
+17
-17
@@ -211,21 +211,21 @@ void main() {
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
// Select a date
|
||||
final today = DateTime.now();
|
||||
await tester.selectDay(content: today.day);
|
||||
final now = DateTime.now();
|
||||
await tester.selectDay(content: now.day);
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: DateFormat('MMM dd, y').format(today),
|
||||
content: DateFormat('MMM dd, y').format(now),
|
||||
);
|
||||
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
|
||||
|
||||
// Toggle include time
|
||||
final now = DateTime.now();
|
||||
// When toggling include time, the time value is from the previous existing date time, not the current time
|
||||
await tester.toggleIncludeTime();
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
@@ -299,7 +299,7 @@ void main() {
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Make sure the option is created and displayed in the cell
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 1',
|
||||
);
|
||||
@@ -311,12 +311,12 @@ void main() {
|
||||
await tester.createOption(name: 'tag 2');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 2',
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsOneWidget,
|
||||
);
|
||||
@@ -328,12 +328,12 @@ void main() {
|
||||
await tester.selectOption(name: 'tag 1');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: 'tag 1',
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsOneWidget,
|
||||
);
|
||||
@@ -345,7 +345,7 @@ void main() {
|
||||
await tester.selectOption(name: 'tag 1');
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNothing,
|
||||
);
|
||||
@@ -378,7 +378,7 @@ void main() {
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Make sure the option is created and displayed in the cell
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags.first,
|
||||
);
|
||||
@@ -393,13 +393,13 @@ void main() {
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
for (final tag in tags) {
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tag,
|
||||
);
|
||||
}
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNWidgets(4),
|
||||
);
|
||||
@@ -413,7 +413,7 @@ void main() {
|
||||
}
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNothing,
|
||||
);
|
||||
@@ -426,16 +426,16 @@ void main() {
|
||||
await tester.selectOption(name: tags[3]);
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags[1],
|
||||
);
|
||||
await tester.findSelectOptionWithNameInGrid(
|
||||
tester.findSelectOptionWithNameInGrid(
|
||||
rowIndex: 0,
|
||||
name: tags[3],
|
||||
);
|
||||
|
||||
await tester.assertNumberOfSelectedOptionsInGrid(
|
||||
tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 0,
|
||||
matcher: findsNWidgets(2),
|
||||
);
|
||||
|
||||
+11
-2
@@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
@@ -10,7 +10,7 @@ import '../../shared/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('database field settings', () {
|
||||
group('grid field settings test:', () {
|
||||
testWidgets('field visibility', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
@@ -50,6 +50,15 @@ void main() {
|
||||
await tester.tapHidePropertyButtonInFieldEditor();
|
||||
await tester.dismissRowDetailPage();
|
||||
tester.noFieldWithName('New field 1');
|
||||
|
||||
// the field should still be sort and filter-able
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(
|
||||
FieldType.RichText,
|
||||
"New field 1",
|
||||
);
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, "New field 1");
|
||||
});
|
||||
});
|
||||
}
|
||||
+212
-4
@@ -15,7 +15,7 @@ import '../../shared/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid field editor:', () {
|
||||
group('grid edit field test:', () {
|
||||
testWidgets('rename existing field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
@@ -24,7 +24,6 @@ void main() {
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Name');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
await tester.renameField('hello world');
|
||||
await tester.dismissFieldEditor();
|
||||
@@ -33,6 +32,32 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('edit field icon', (tester) async {
|
||||
const icon = 'artificial_intelligence/ai-upscale-spark';
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
tester.assertFieldSvg('Name', FieldType.RichText);
|
||||
|
||||
// choose specific icon
|
||||
await tester.tapGridFieldWithName('Name');
|
||||
await tester.changeFieldIcon(icon);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
tester.assertFieldCustomSvg('Name', icon);
|
||||
|
||||
// remove icon
|
||||
await tester.tapGridFieldWithName('Name');
|
||||
await tester.changeFieldIcon('');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
tester.assertFieldSvg('Name', FieldType.RichText);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('update field type of existing field', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
@@ -126,7 +151,7 @@ void main() {
|
||||
await tester.dismissFieldEditor();
|
||||
tester.findFieldWithName('Right');
|
||||
|
||||
// insert new field to the right
|
||||
// insert new field to the left
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapInsertFieldButton(left: true, name: "Left");
|
||||
await tester.dismissFieldEditor();
|
||||
@@ -245,7 +270,6 @@ void main() {
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await tester.enterText(inputField, text);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||
|
||||
@@ -357,5 +381,189 @@ void main() {
|
||||
// content: DateFormat('dd/MM/y hh:mm a').format(modified),
|
||||
// );
|
||||
// });
|
||||
|
||||
testWidgets('select option transform', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
layout: ViewLayoutPB.Grid,
|
||||
);
|
||||
|
||||
// invoke the field editor of existing Single-Select field Type
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// add some select options
|
||||
await tester.tapAddSelectOptionButton();
|
||||
for (final optionName in ['A', 'B', 'C']) {
|
||||
final inputField = find.descendant(
|
||||
of: find.byType(CreateOptionTextField),
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await tester.enterText(inputField, optionName);
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
}
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// select A in first row's cell under the Type field
|
||||
await tester.tapCellInGrid(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.SingleSelect,
|
||||
);
|
||||
await tester.selectOption(name: 'A');
|
||||
await tester.dismissCellEditor();
|
||||
tester.findSelectOptionWithNameInGrid(name: 'A', rowIndex: 0);
|
||||
|
||||
await tester.changeFieldTypeOfFieldWithName('Type', FieldType.RichText);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: "A",
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
// add some random text in the second row
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.RichText,
|
||||
input: "random",
|
||||
cellIndex: 1,
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.RichText,
|
||||
content: "random",
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
'Type',
|
||||
FieldType.SingleSelect,
|
||||
);
|
||||
tester.findSelectOptionWithNameInGrid(name: 'A', rowIndex: 0);
|
||||
tester.assertNumberOfSelectedOptionsInGrid(
|
||||
rowIndex: 1,
|
||||
matcher: findsNothing,
|
||||
);
|
||||
|
||||
// create a new field for testing
|
||||
await tester.createField(FieldType.RichText, name: 'Test');
|
||||
|
||||
// edit the first 2 rows
|
||||
await tester.editCell(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
input: "E,F",
|
||||
cellIndex: 1,
|
||||
);
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.RichText,
|
||||
input: "G",
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
'Test',
|
||||
FieldType.MultiSelect,
|
||||
);
|
||||
tester.assertMultiSelectOption(contents: ['E', 'F'], rowIndex: 0);
|
||||
tester.assertMultiSelectOption(contents: ['G'], rowIndex: 1);
|
||||
|
||||
await tester.tapCellInGrid(
|
||||
rowIndex: 2,
|
||||
fieldType: FieldType.MultiSelect,
|
||||
);
|
||||
await tester.selectOption(name: 'G');
|
||||
await tester.createOption(name: 'H');
|
||||
await tester.dismissCellEditor();
|
||||
tester.findSelectOptionWithNameInGrid(name: 'A', rowIndex: 0);
|
||||
tester.assertMultiSelectOption(contents: ['G', 'H'], rowIndex: 2);
|
||||
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
'Test',
|
||||
FieldType.RichText,
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 2,
|
||||
fieldType: FieldType.RichText,
|
||||
content: "G,H",
|
||||
cellIndex: 1,
|
||||
);
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
'Test',
|
||||
FieldType.MultiSelect,
|
||||
);
|
||||
|
||||
tester.assertMultiSelectOption(contents: ['E', 'F'], rowIndex: 0);
|
||||
tester.assertMultiSelectOption(contents: ['G'], rowIndex: 1);
|
||||
tester.assertMultiSelectOption(contents: ['G', 'H'], rowIndex: 2);
|
||||
});
|
||||
|
||||
testWidgets('date time transform', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
// create a date field
|
||||
await tester.createField(FieldType.DateTime);
|
||||
|
||||
// edit the first date cell
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
|
||||
await tester.toggleIncludeTime();
|
||||
final now = DateTime.now();
|
||||
await tester.selectDay(content: now.day);
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: DateFormat('MMM dd, y HH:mm').format(now),
|
||||
);
|
||||
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
'Date',
|
||||
FieldType.RichText,
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.RichText,
|
||||
content: DateFormat('MMM dd, y HH:mm').format(now),
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.RichText,
|
||||
input: "Oct 5, 2024",
|
||||
cellIndex: 1,
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.RichText,
|
||||
content: "Oct 5, 2024",
|
||||
cellIndex: 1,
|
||||
);
|
||||
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
'Date',
|
||||
FieldType.DateTime,
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 0,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: DateFormat('MMM dd, y').format(now),
|
||||
// content: DateFormat('MMM dd, y HH:mm').format(now),
|
||||
);
|
||||
tester.assertCellContent(
|
||||
rowIndex: 1,
|
||||
fieldType: FieldType.DateTime,
|
||||
content: "Oct 05, 2024",
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+80
-23
@@ -1,17 +1,21 @@
|
||||
import 'package:appflowy/plugins/database/application/field/filter_entities.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/text.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/database_test_op.dart';
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid filter:', () {
|
||||
testWidgets('add text filter', (tester) async {
|
||||
await tester.openV020database();
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
@@ -19,68 +23,68 @@ void main() {
|
||||
await tester.tapFilterButtonInGrid('Name');
|
||||
|
||||
// enter 'A' in the filter text field
|
||||
await tester.assertNumberOfRowsInGridPage(10);
|
||||
tester.assertNumberOfRowsInGridPage(10);
|
||||
await tester.enterTextInTextFilter('A');
|
||||
await tester.assertNumberOfRowsInGridPage(1);
|
||||
tester.assertNumberOfRowsInGridPage(1);
|
||||
|
||||
// after remove the filter, the grid should show all rows
|
||||
await tester.enterTextInTextFilter('');
|
||||
await tester.assertNumberOfRowsInGridPage(10);
|
||||
tester.assertNumberOfRowsInGridPage(10);
|
||||
|
||||
await tester.enterTextInTextFilter('B');
|
||||
await tester.assertNumberOfRowsInGridPage(1);
|
||||
tester.assertNumberOfRowsInGridPage(1);
|
||||
|
||||
// open the menu to delete the filter
|
||||
await tester.tapDisclosureButtonInFinder(find.byType(TextFilterEditor));
|
||||
await tester.tapDeleteFilterButtonInGrid();
|
||||
await tester.assertNumberOfRowsInGridPage(10);
|
||||
tester.assertNumberOfRowsInGridPage(10);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add checkbox filter', (tester) async {
|
||||
await tester.openV020database();
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(FieldType.Checkbox, 'Done');
|
||||
await tester.assertNumberOfRowsInGridPage(5);
|
||||
tester.assertNumberOfRowsInGridPage(5);
|
||||
|
||||
await tester.tapFilterButtonInGrid('Done');
|
||||
await tester.tapCheckboxFilterButtonInGrid();
|
||||
|
||||
await tester.tapUnCheckedButtonOnCheckboxFilter();
|
||||
await tester.assertNumberOfRowsInGridPage(5);
|
||||
tester.assertNumberOfRowsInGridPage(5);
|
||||
|
||||
await tester
|
||||
.tapDisclosureButtonInFinder(find.byType(CheckboxFilterEditor));
|
||||
await tester.tapDeleteFilterButtonInGrid();
|
||||
await tester.assertNumberOfRowsInGridPage(10);
|
||||
tester.assertNumberOfRowsInGridPage(10);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add checklist filter', (tester) async {
|
||||
await tester.openV020database();
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(FieldType.Checklist, 'checklist');
|
||||
|
||||
// By default, the condition of checklist filter is 'uncompleted'
|
||||
await tester.assertNumberOfRowsInGridPage(9);
|
||||
tester.assertNumberOfRowsInGridPage(9);
|
||||
|
||||
await tester.tapFilterButtonInGrid('checklist');
|
||||
await tester.tapChecklistFilterButtonInGrid();
|
||||
|
||||
await tester.tapCompletedButtonOnChecklistFilter();
|
||||
await tester.assertNumberOfRowsInGridPage(1);
|
||||
tester.assertNumberOfRowsInGridPage(1);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add single select filter', (tester) async {
|
||||
await tester.openV020database();
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
@@ -90,27 +94,27 @@ void main() {
|
||||
|
||||
// select the option 's6'
|
||||
await tester.tapOptionFilterWithName('s6');
|
||||
await tester.assertNumberOfRowsInGridPage(0);
|
||||
tester.assertNumberOfRowsInGridPage(0);
|
||||
|
||||
// unselect the option 's6'
|
||||
await tester.tapOptionFilterWithName('s6');
|
||||
await tester.assertNumberOfRowsInGridPage(10);
|
||||
tester.assertNumberOfRowsInGridPage(10);
|
||||
|
||||
// select the option 's5'
|
||||
await tester.tapOptionFilterWithName('s5');
|
||||
await tester.assertNumberOfRowsInGridPage(1);
|
||||
tester.assertNumberOfRowsInGridPage(1);
|
||||
|
||||
// select the option 's4'
|
||||
await tester.tapOptionFilterWithName('s4');
|
||||
|
||||
// The row with 's4' should be shown.
|
||||
await tester.assertNumberOfRowsInGridPage(1);
|
||||
tester.assertNumberOfRowsInGridPage(1);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add multi select filter', (tester) async {
|
||||
await tester.openV020database();
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
@@ -124,17 +128,70 @@ void main() {
|
||||
|
||||
// select the option 'm1'. Any option with 'm1' should be shown.
|
||||
await tester.tapOptionFilterWithName('m1');
|
||||
await tester.assertNumberOfRowsInGridPage(5);
|
||||
tester.assertNumberOfRowsInGridPage(5);
|
||||
await tester.tapOptionFilterWithName('m1');
|
||||
|
||||
// select the option 'm2'. Any option with 'm2' should be shown.
|
||||
await tester.tapOptionFilterWithName('m2');
|
||||
await tester.assertNumberOfRowsInGridPage(4);
|
||||
tester.assertNumberOfRowsInGridPage(4);
|
||||
await tester.tapOptionFilterWithName('m2');
|
||||
|
||||
// select the option 'm4'. Any option with 'm4' should be shown.
|
||||
await tester.tapOptionFilterWithName('m4');
|
||||
await tester.assertNumberOfRowsInGridPage(1);
|
||||
tester.assertNumberOfRowsInGridPage(1);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add date filter', (tester) async {
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(FieldType.DateTime, 'date');
|
||||
|
||||
// By default, the condition of date filter is current day and time
|
||||
tester.assertNumberOfRowsInGridPage(0);
|
||||
|
||||
await tester.tapFilterButtonInGrid('date');
|
||||
await tester.tapDateFilterButtonInGrid();
|
||||
await tester.tapDateFilterCondition(DateTimeFilterCondition.before);
|
||||
tester.assertNumberOfRowsInGridPage(7);
|
||||
|
||||
await tester.tapDateFilterButtonInGrid();
|
||||
await tester.tapDateFilterCondition(DateTimeFilterCondition.isEmpty);
|
||||
tester.assertNumberOfRowsInGridPage(3);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add timestamp filter', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.scrollToRight(find.byType(GridPage));
|
||||
|
||||
await tester.createField(
|
||||
FieldType.CreatedTime,
|
||||
name: 'Created at',
|
||||
);
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(
|
||||
FieldType.CreatedTime,
|
||||
'Created at',
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
tester.assertNumberOfRowsInGridPage(3);
|
||||
|
||||
await tester.tapFilterButtonInGrid('Created at');
|
||||
await tester.tapDateFilterButtonInGrid();
|
||||
await tester.tapDateFilterCondition(DateTimeFilterCondition.before);
|
||||
tester.assertNumberOfRowsInGridPage(0);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
+217
-1
@@ -4,9 +4,12 @@ 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/database/widgets/cell_editor/media_cell_editor.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/toggle/toggle.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@@ -18,12 +21,13 @@ import 'package:path_provider/path_provider.dart';
|
||||
import '../../shared/database_test_op.dart';
|
||||
import '../../shared/mock/mock_file_picker.dart';
|
||||
import '../../shared/util.dart';
|
||||
import '../board/board_hide_groups_test.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('media type option in database', () {
|
||||
testWidgets('add media field and add files', (tester) async {
|
||||
testWidgets('add media field and add files two times', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
@@ -92,5 +96,217 @@ void main() {
|
||||
// Remove the temp files
|
||||
await Future.wait([firstFile.delete(), secondFile.delete()]);
|
||||
});
|
||||
|
||||
testWidgets('add two files at once', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// Change to media type
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.Media);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Open media cell editor
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.Media);
|
||||
await tester.findMediaCellEditor(findsOneWidget);
|
||||
|
||||
// Prepare files for upload from local
|
||||
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');
|
||||
|
||||
// Click on add file button in the Media Cell Editor
|
||||
await tester.tap(find.text(LocaleKeys.grid_media_addFileOrImage.tr()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap on the upload interaction
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_plugins_file_fileUploadHint.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect two files
|
||||
expect(find.byType(RenderMedia), findsNWidgets(2));
|
||||
|
||||
// Remove the temp files
|
||||
await Future.wait([firstFile.delete(), secondFile.delete()]);
|
||||
});
|
||||
|
||||
testWidgets('delete files', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// Change to media type
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.Media);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Open media cell editor
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.Media);
|
||||
await tester.findMediaCellEditor(findsOneWidget);
|
||||
|
||||
// Prepare files for upload from local
|
||||
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');
|
||||
|
||||
// Click on add file button in the Media Cell Editor
|
||||
await tester.tap(find.text(LocaleKeys.grid_media_addFileOrImage.tr()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap on the upload interaction
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_plugins_file_fileUploadHint.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect two files
|
||||
expect(find.byType(RenderMedia), findsNWidgets(2));
|
||||
|
||||
// Tap on the three dots menu for the first RenderMedia
|
||||
final mediaMenuFinder = find.descendant(
|
||||
of: find.byType(RenderMedia),
|
||||
matching: find.byFlowySvg(FlowySvgs.three_dots_s),
|
||||
);
|
||||
|
||||
await tester.tap(mediaMenuFinder.first);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap on the delete button
|
||||
await tester.tap(find.text(LocaleKeys.grid_media_delete.tr()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap on Delete button in the confirmation dialog
|
||||
await tester.tap(
|
||||
find.descendant(
|
||||
of: find.byType(SpaceCancelOrConfirmButton),
|
||||
matching: find.text(LocaleKeys.grid_media_delete.tr()),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
// Expect one file
|
||||
expect(find.byType(RenderMedia), findsOneWidget);
|
||||
|
||||
// Remove the temp files
|
||||
await Future.wait([firstFile.delete(), secondFile.delete()]);
|
||||
});
|
||||
|
||||
testWidgets('hide file names', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// Change to media type
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.Media);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Open media cell editor
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.Media);
|
||||
await tester.findMediaCellEditor(findsOneWidget);
|
||||
|
||||
// Prepare files for upload from local
|
||||
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');
|
||||
|
||||
// Click on add file button in the Media Cell Editor
|
||||
await tester.tap(find.text(LocaleKeys.grid_media_addFileOrImage.tr()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap on the upload interaction
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_plugins_file_fileUploadHint.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect two files
|
||||
expect(find.byType(RenderMedia), findsNWidgets(2));
|
||||
|
||||
await tester.dismissCellEditor();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Open first row in row detail view then toggle hide file names
|
||||
await tester.openFirstRowDetailPage();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect file names to be shown
|
||||
expect(find.text('sample.jpeg'), findsOneWidget);
|
||||
expect(find.text('sample.gif'), findsOneWidget);
|
||||
|
||||
await tester.tapGridFieldWithNameInRowDetailPage('Type');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Toggle hide file names
|
||||
await tester.tap(find.byType(Toggle));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.dismissRowDetailPage();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect file names to be hidden
|
||||
expect(find.text('sample.jpeg'), findsNothing);
|
||||
expect(find.text('sample.gif'), findsNothing);
|
||||
|
||||
// Remove the temp files
|
||||
await Future.wait([firstFile.delete(), secondFile.delete()]);
|
||||
});
|
||||
});
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/card/card.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell_editor/media_cell_editor.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_banner.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
|
||||
import 'package:appflowy/shared/af_image.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
import '../../shared/database_test_op.dart';
|
||||
import '../../shared/mock/mock_file_picker.dart';
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('database row cover', () {
|
||||
testWidgets('add image to media field and check if cover is set (grid)',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Invoke the field editor
|
||||
await tester.tapGridFieldWithName('Type');
|
||||
await tester.tapEditFieldButton();
|
||||
|
||||
// Change to media type
|
||||
await tester.tapSwitchFieldTypeButton();
|
||||
await tester.selectFieldType(FieldType.Media);
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// Prepare file for upload from local
|
||||
final image = await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final tempDirectory = await getTemporaryDirectory();
|
||||
|
||||
final imagePath = p.join(tempDirectory.path, 'sample.jpeg');
|
||||
final file = File(imagePath)
|
||||
..writeAsBytesSync(image.buffer.asUint8List());
|
||||
|
||||
mockPickFilePaths(paths: [imagePath]);
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
|
||||
|
||||
// Open media cell editor
|
||||
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.Media);
|
||||
await tester.findMediaCellEditor(findsOneWidget);
|
||||
|
||||
// Click on add file button in the Media Cell Editor
|
||||
await tester.tap(find.text(LocaleKeys.grid_media_addFileOrImage.tr()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap on the upload interaction
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_plugins_file_fileUploadHint.tr(),
|
||||
);
|
||||
|
||||
// Expect one file
|
||||
expect(find.byType(RenderMedia), findsOneWidget);
|
||||
|
||||
// Close cell editor
|
||||
await tester.dismissCellEditor();
|
||||
|
||||
// Open first row in row detail view
|
||||
await tester.openFirstRowDetailPage();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect a cover to be shown
|
||||
expect(find.byType(RowCover), findsOneWidget);
|
||||
|
||||
// Remove the temp file
|
||||
await Future.wait([file.delete()]);
|
||||
});
|
||||
|
||||
testWidgets('add and remove cover from Row Detail Card', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Open first row in row detail view
|
||||
await tester.openFirstRowDetailPage();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect no cover
|
||||
expect(find.byType(RowCover), findsNothing);
|
||||
|
||||
// Hover on RowBanner to show Add Cover button
|
||||
await tester.hoverRowBanner();
|
||||
|
||||
// Click on Add Cover button
|
||||
await tester.tapAddCoverButton();
|
||||
|
||||
// Expect a cover to be shown - the default asset cover
|
||||
expect(find.byType(RowCover), findsOneWidget);
|
||||
|
||||
// Tap on the delete cover button
|
||||
await tester.tapButton(find.byType(DeleteCoverButton));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect no cover to be shown
|
||||
expect(find.byType(AFImage), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('add and change cover and check in Board', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Open "Card 1"
|
||||
await tester.tap(find.text('Card 1'), warnIfMissed: false);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Expect no cover
|
||||
expect(find.byType(RowCover), findsNothing);
|
||||
|
||||
// Hover on RowBanner to show Add Cover button
|
||||
await tester.hoverRowBanner();
|
||||
|
||||
// Click on Add Cover button
|
||||
await tester.tapAddCoverButton();
|
||||
|
||||
// Expect default cover to be shown
|
||||
expect(find.byType(RowCover), findsOneWidget);
|
||||
|
||||
// Prepare image for upload from local
|
||||
final image = await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final tempDirectory = await getTemporaryDirectory();
|
||||
final imagePath = p.join(tempDirectory.path, 'sample.jpeg');
|
||||
final file = File(imagePath)
|
||||
..writeAsBytesSync(image.buffer.asUint8List());
|
||||
|
||||
mockPickFilePaths(paths: [imagePath]);
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, '0');
|
||||
|
||||
// Hover on RowBanner to show Change Cover button
|
||||
await tester.hoverRowBanner();
|
||||
|
||||
// Tap on the change cover button
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_plugins_cover_changeCover.tr(),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Change tab to Upload tab
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_upload_label.tr(),
|
||||
);
|
||||
|
||||
// Tab on the upload button
|
||||
await tester.tapButtonWithName(
|
||||
LocaleKeys.document_imageBlock_upload_placeholder.tr(),
|
||||
);
|
||||
|
||||
// Expect one cover
|
||||
expect(find.byType(RowCover), findsOneWidget);
|
||||
|
||||
// Expect the cover to be shown both in RowCover and in CardCover
|
||||
expect(find.byType(AFImage), findsNWidgets(2));
|
||||
|
||||
// Dismiss Row Detail Page
|
||||
await tester.dismissRowDetailPage();
|
||||
|
||||
// Expect a cover to be shown in CardCover
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(CardCover),
|
||||
matching: find.byType(AFImage),
|
||||
),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// Remove the temp file
|
||||
await Future.wait([file.delete()]);
|
||||
});
|
||||
});
|
||||
}
|
||||
+24
-29
@@ -1,12 +1,14 @@
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/desktop_field_cell.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_banner.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/desktop_field_cell.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_detail.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
@@ -36,26 +38,7 @@ void main() {
|
||||
await tester.assertDocumentExistInRowDetailPage();
|
||||
});
|
||||
|
||||
testWidgets('add emoji', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// Create a new grid
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
// Hover first row and then open the row page
|
||||
await tester.openFirstRowDetailPage();
|
||||
|
||||
await tester.hoverRowBanner();
|
||||
|
||||
await tester.openEmojiPicker();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// After select the emoji, the EmojiButton will show up
|
||||
await tester.tapButton(find.byType(EmojiButton));
|
||||
});
|
||||
|
||||
testWidgets('update emoji', (tester) async {
|
||||
testWidgets('add and update emoji', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
@@ -68,8 +51,18 @@ void main() {
|
||||
await tester.openEmojiPicker();
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Update existing selected emoji
|
||||
await tester.tapButton(find.byType(EmojiButton));
|
||||
// expect to find the emoji selected
|
||||
final firstEmojiFinder = find.byWidgetPredicate(
|
||||
(w) => w is FlowyText && w.text == '😀',
|
||||
);
|
||||
|
||||
// There are 2 eomjis - one in the row banner and another in the primary cell
|
||||
expect(firstEmojiFinder, findsNWidgets(2));
|
||||
|
||||
// Update existing selected emoji - tap on it to update
|
||||
await tester.tapButton(find.byType(EmojiIconWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tapEmoji('😅');
|
||||
|
||||
// The emoji already displayed in the row banner
|
||||
@@ -96,7 +89,9 @@ void main() {
|
||||
await tester.tapEmoji('😀');
|
||||
|
||||
// Remove the emoji
|
||||
await tester.tapButton(find.byType(RemoveEmojiButton));
|
||||
await tester.tapButton(find.byType(EmojiIconWidget));
|
||||
await tester.tapButton(find.text(LocaleKeys.button_remove.tr()));
|
||||
|
||||
final emojiText = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowyText && widget.text == '😀',
|
||||
);
|
||||
@@ -321,7 +316,7 @@ void main() {
|
||||
await tester.tapRowDetailPageDeleteRowButton();
|
||||
await tester.tapEscButton();
|
||||
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
tester.assertNumberOfRowsInGridPage(2);
|
||||
});
|
||||
|
||||
testWidgets('duplicate row', (tester) async {
|
||||
@@ -338,7 +333,7 @@ void main() {
|
||||
await tester.tapRowDetailPageDuplicateRowButton();
|
||||
await tester.tapEscButton();
|
||||
|
||||
await tester.assertNumberOfRowsInGridPage(4);
|
||||
tester.assertNumberOfRowsInGridPage(4);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
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('grid', () {
|
||||
testWidgets('create row of the grid', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
await tester.tapCreateRowButtonInGrid();
|
||||
|
||||
// 3 initial rows + 1 created
|
||||
await tester.assertNumberOfRowsInGridPage(4);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('create row from row menu of the grid', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
|
||||
await tester.tapCreateRowButtonInRowMenuOfGrid();
|
||||
|
||||
// 3 initial rows + 1 created
|
||||
await tester.assertNumberOfRowsInGridPage(4);
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('delete row of the grid', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
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();
|
||||
|
||||
// 3 initial rows - 1 deleted
|
||||
await tester.assertNumberOfRowsInGridPage(2);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('check number of row indicator in the initial grid',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
});
|
||||
}
|
||||
+2
-2
@@ -9,7 +9,7 @@ void main() {
|
||||
|
||||
group('database', () {
|
||||
testWidgets('import v0.2.0 database data', (tester) async {
|
||||
await tester.openV020database();
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
// wait the database data is loaded
|
||||
await tester.pumpAndSettle(const Duration(microseconds: 500));
|
||||
|
||||
@@ -115,7 +115,7 @@ void main() {
|
||||
[],
|
||||
];
|
||||
for (final (index, contents) in multiSelectCells.indexed) {
|
||||
await tester.assertMultiSelectOption(
|
||||
tester.assertMultiSelectOption(
|
||||
rowIndex: index,
|
||||
contents: contents,
|
||||
);
|
||||
|
||||
+115
-18
@@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_editor.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
@@ -7,9 +8,9 @@ import '../../shared/database_test_op.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid', () {
|
||||
testWidgets('add text sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
group('grid sort:', () {
|
||||
testWidgets('text sort', (tester) async {
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
|
||||
@@ -37,7 +38,7 @@ void main() {
|
||||
|
||||
// open the sort menu and select order by descending
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapSortButtonByName('Name');
|
||||
await tester.tapEditSortConditionButtonByFieldName('Name');
|
||||
await tester.tapSortByDescending();
|
||||
for (final (index, content) in <String>[
|
||||
'E',
|
||||
@@ -60,7 +61,7 @@ void main() {
|
||||
|
||||
// delete all sorts
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapAllSortButton();
|
||||
await tester.tapDeleteAllSortsButton();
|
||||
|
||||
// check the text cell order
|
||||
for (final (index, content) in <String>[
|
||||
@@ -84,8 +85,8 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add checkbox sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
testWidgets('checkbox', (tester) async {
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Checkbox, 'Done');
|
||||
@@ -111,7 +112,7 @@ void main() {
|
||||
|
||||
// open the sort menu and select order by descending
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapSortButtonByName('Done');
|
||||
await tester.tapEditSortConditionButtonByFieldName('Done');
|
||||
await tester.tapSortByDescending();
|
||||
for (final (index, content) in <bool>[
|
||||
true,
|
||||
@@ -134,8 +135,8 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add number sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
testWidgets('number', (tester) async {
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Number, 'number');
|
||||
@@ -162,7 +163,7 @@ void main() {
|
||||
|
||||
// open the sort menu and select order by descending
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapSortButtonByName('number');
|
||||
await tester.tapEditSortConditionButtonByFieldName('number');
|
||||
await tester.tapSortByDescending();
|
||||
for (final (index, content) in <String>[
|
||||
'12',
|
||||
@@ -186,15 +187,15 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
|
||||
testWidgets('add checkbox and number sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
testWidgets('checkbox and number', (tester) async {
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Checkbox, 'Done');
|
||||
|
||||
// open the sort menu and sort checkbox by descending
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapSortButtonByName('Done');
|
||||
await tester.tapEditSortConditionButtonByFieldName('Done');
|
||||
await tester.tapSortByDescending();
|
||||
for (final (index, content) in <bool>[
|
||||
true,
|
||||
@@ -220,7 +221,7 @@ void main() {
|
||||
FieldType.Number,
|
||||
'number',
|
||||
);
|
||||
await tester.tapSortButtonByName('number');
|
||||
await tester.tapEditSortConditionButtonByFieldName('number');
|
||||
await tester.tapSortByDescending();
|
||||
|
||||
// check checkbox cell order
|
||||
@@ -266,14 +267,14 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('reorder sort', (tester) async {
|
||||
await tester.openV020database();
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
// create a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Checkbox, 'Done');
|
||||
|
||||
// open the sort menu and sort checkbox by descending
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapSortButtonByName('Done');
|
||||
await tester.tapEditSortConditionButtonByFieldName('Done');
|
||||
await tester.tapSortByDescending();
|
||||
|
||||
// add another sort, this time by number descending
|
||||
@@ -282,7 +283,7 @@ void main() {
|
||||
FieldType.Number,
|
||||
'number',
|
||||
);
|
||||
await tester.tapSortButtonByName('number');
|
||||
await tester.tapEditSortConditionButtonByFieldName('number');
|
||||
await tester.tapSortByDescending();
|
||||
|
||||
// check checkbox cell order
|
||||
@@ -370,5 +371,101 @@ void main() {
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('edit field', (tester) async {
|
||||
await tester.openTestDatabase(v020GridFileName);
|
||||
|
||||
// create a number sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.Number, 'number');
|
||||
|
||||
// check the number cell order
|
||||
for (final (index, content) in <String>[
|
||||
'-2',
|
||||
'-1',
|
||||
'0.1',
|
||||
'0.2',
|
||||
'1',
|
||||
'2',
|
||||
'10',
|
||||
'11',
|
||||
'12',
|
||||
'',
|
||||
].indexed) {
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.Number,
|
||||
content: content,
|
||||
);
|
||||
}
|
||||
|
||||
final textCells = <String>[
|
||||
'B',
|
||||
'A',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
];
|
||||
for (final (index, content) in textCells.indexed) {
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.RichText,
|
||||
content: content,
|
||||
);
|
||||
}
|
||||
|
||||
// edit the name of the number field
|
||||
await tester.tapGridFieldWithName('number');
|
||||
|
||||
await tester.renameField('hello world');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
await tester.tapGridFieldWithName('hello world');
|
||||
await tester.dismissFieldEditor();
|
||||
|
||||
// expect name to be changed as well
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
final sortItem = find.ancestor(
|
||||
of: find.text('hello world'),
|
||||
matching: find.byType(DatabaseSortItem),
|
||||
);
|
||||
expect(sortItem, findsOneWidget);
|
||||
|
||||
// change the field type of the field to checkbox
|
||||
await tester.tapGridFieldWithName('hello world');
|
||||
await tester.changeFieldTypeOfFieldWithName(
|
||||
'hello world',
|
||||
FieldType.Checkbox,
|
||||
);
|
||||
|
||||
// expect name to be changed as well
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
expect(sortItem, findsOneWidget);
|
||||
|
||||
final newTextCells = <String>[
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
];
|
||||
for (final (index, content) in newTextCells.indexed) {
|
||||
tester.assertCellContent(
|
||||
rowIndex: index,
|
||||
fieldType: FieldType.RichText,
|
||||
content: content,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Editor AppLifeCycle tests', () {
|
||||
testWidgets(
|
||||
'Selection is added back after pausing AppFlowy',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
final selection = Selection.single(path: [4], startOffset: 0);
|
||||
await tester.editor.updateSelection(selection);
|
||||
|
||||
binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive);
|
||||
expect(tester.editor.getCurrentEditorState().selection, null);
|
||||
|
||||
binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.editor.getCurrentEditorState().selection, selection);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Null selection is retained after pausing AppFlowy',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
final selection = Selection.single(path: [4], startOffset: 0);
|
||||
await tester.editor.updateSelection(selection);
|
||||
await tester.editor.updateSelection(null);
|
||||
|
||||
binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive);
|
||||
expect(tester.editor.getCurrentEditorState().selection, null);
|
||||
|
||||
binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.editor.getCurrentEditorState().selection, null);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'Non-collapsed selection is retained after pausing AppFlowy',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
final selection = Selection(
|
||||
start: Position(path: [3]),
|
||||
end: Position(path: [3], offset: 8),
|
||||
);
|
||||
await tester.editor.updateSelection(selection);
|
||||
|
||||
binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive);
|
||||
binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(tester.editor.getCurrentEditorState().selection, selection);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
+4
-2
@@ -13,13 +13,15 @@ import '../../shared/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('paste in codeblock', () {
|
||||
group('paste in codeblock:', () {
|
||||
testWidgets('paste multiple lines in codeblock', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'Test Document');
|
||||
// focus on the editor
|
||||
await tester.tapButton(find.byType(AppFlowyEditor));
|
||||
|
||||
// mock the clipboard
|
||||
const lines = 3;
|
||||
|
||||
+164
-15
@@ -1,12 +1,19 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/block_menu/block_menu_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.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/link_preview/custom_link_preview.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:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
@@ -207,7 +214,7 @@ void main() {
|
||||
final image = await rootBundle.load('assets/test/images/sample.png');
|
||||
final bytes = image.buffer.asUint8List();
|
||||
await tester.pasteContent(image: ('png', bytes), (editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
expect(editorState.document.root.children.length, 1);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
expect(node.attributes[ImageBlockKeys.url], isNotNull);
|
||||
@@ -218,7 +225,7 @@ void main() {
|
||||
final image = await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final bytes = image.buffer.asUint8List();
|
||||
await tester.pasteContent(image: ('jpeg', bytes), (editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
expect(editorState.document.root.children.length, 1);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
expect(node.attributes[ImageBlockKeys.url], isNotNull);
|
||||
@@ -226,15 +233,14 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('paste image(gif) from memory', (tester) async {
|
||||
// It's not supported yet.
|
||||
// final image = await rootBundle.load('assets/test/images/sample.gif');
|
||||
// final bytes = image.buffer.asUint8List();
|
||||
// await tester.pasteContent(image: ('gif', bytes), (editorState) {
|
||||
// expect(editorState.document.root.children.length, 2);
|
||||
// final node = editorState.getNodeAtPath([0])!;
|
||||
// expect(node.type, ImageBlockKeys.type);
|
||||
// expect(node.attributes[ImageBlockKeys.url], isNotNull);
|
||||
// });
|
||||
final image = await rootBundle.load('assets/test/images/sample.gif');
|
||||
final bytes = image.buffer.asUint8List();
|
||||
await tester.pasteContent(image: ('gif', bytes), (editorState) {
|
||||
expect(editorState.document.root.children.length, 1);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
expect(node.attributes[ImageBlockKeys.url], isNotNull);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
@@ -280,7 +286,7 @@ void main() {
|
||||
html: html,
|
||||
image: ('png', bytes),
|
||||
(editorState) {
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
expect(editorState.document.root.children.length, 1);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
},
|
||||
@@ -312,14 +318,155 @@ void main() {
|
||||
'auto convert url to link preview block',
|
||||
(tester) async {
|
||||
const url = 'https://appflowy.io';
|
||||
await tester.pasteContent(plainText: url, (editorState) {
|
||||
await tester.pasteContent(plainText: url, (editorState) async {
|
||||
// the second one is the paragraph node
|
||||
expect(editorState.document.root.children.length, 2);
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, LinkPreviewBlockKeys.type);
|
||||
expect(node.attributes[LinkPreviewBlockKeys.url], url);
|
||||
});
|
||||
|
||||
// hover on the link preview block
|
||||
// click the more button
|
||||
// and select convert to link
|
||||
await tester.hoverOnWidget(
|
||||
find.byType(CustomLinkPreviewWidget),
|
||||
onHover: () async {
|
||||
final convertToLinkButton = find.byWidgetPredicate((widget) {
|
||||
return widget is MenuBlockButton &&
|
||||
widget.tooltip ==
|
||||
LocaleKeys.document_plugins_urlPreview_convertToLink.tr();
|
||||
});
|
||||
expect(convertToLinkButton, findsOneWidget);
|
||||
await tester.tap(convertToLinkButton);
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final textNode = editorState.getNodeAtPath([0])!;
|
||||
expect(textNode.type, ParagraphBlockKeys.type);
|
||||
expect(textNode.delta!.toJson(), [
|
||||
{
|
||||
'insert': url,
|
||||
'attributes': {'href': url},
|
||||
}
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'ctrl/cmd+z to undo the auto convert url to link preview block',
|
||||
(tester) async {
|
||||
const url = 'https://appflowy.io';
|
||||
await tester.pasteContent(plainText: url, (editorState) async {
|
||||
// the second one is the paragraph node
|
||||
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.editor.tapLineOfEditorAt(0);
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyZ,
|
||||
isControlPressed:
|
||||
UniversalPlatform.isLinux || UniversalPlatform.isWindows,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, ParagraphBlockKeys.type);
|
||||
expect(node.delta!.toJson(), [
|
||||
{
|
||||
'insert': url,
|
||||
'attributes': {'href': url},
|
||||
}
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'paste the nodes start with non-delta node',
|
||||
(tester) async {
|
||||
await tester.pasteContent((_) {});
|
||||
const text = 'Hello World';
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final transaction = editorState.transaction;
|
||||
// [image_block]
|
||||
// [paragraph_block]
|
||||
transaction.insertNodes([
|
||||
0,
|
||||
], [
|
||||
customImageNode(url: ''),
|
||||
paragraphNode(text: text),
|
||||
]);
|
||||
await editorState.apply(transaction);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
// select all and copy
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyA,
|
||||
isControlPressed:
|
||||
UniversalPlatform.isLinux || UniversalPlatform.isWindows,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyC,
|
||||
isControlPressed:
|
||||
UniversalPlatform.isLinux || UniversalPlatform.isWindows,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
|
||||
// put the cursor to the end of the paragraph block
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
// paste the content
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyV,
|
||||
isControlPressed:
|
||||
UniversalPlatform.isLinux || UniversalPlatform.isWindows,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// expect the image and the paragraph block are inserted below the cursor
|
||||
expect(editorState.getNodeAtPath([0])!.type, CustomImageBlockKeys.type);
|
||||
expect(editorState.getNodeAtPath([1])!.type, ParagraphBlockKeys.type);
|
||||
expect(editorState.getNodeAtPath([2])!.type, CustomImageBlockKeys.type);
|
||||
expect(editorState.getNodeAtPath([3])!.type, ParagraphBlockKeys.type);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('paste the url without protocol', (tester) async {
|
||||
// paste the image that from local file
|
||||
const plainText = '1.jpg';
|
||||
final image = await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final bytes = image.buffer.asUint8List();
|
||||
await tester.pasteContent(plainText: plainText, image: ('jpeg', bytes),
|
||||
(editorState) {
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
expect(node.attributes[ImageBlockKeys.url], isNotEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('paste the image url', (tester) async {
|
||||
const plainText = 'https://appflowy.io/1.jpg';
|
||||
final image = await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
final bytes = image.buffer.asUint8List();
|
||||
await tester.pasteContent(plainText: plainText, image: ('jpeg', bytes),
|
||||
(editorState) {
|
||||
final node = editorState.getNodeAtPath([0])!;
|
||||
expect(node.type, ImageBlockKeys.type);
|
||||
expect(node.attributes[ImageBlockKeys.url], isNotEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
extension on WidgetTester {
|
||||
@@ -335,7 +482,9 @@ extension on WidgetTester {
|
||||
await tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await createNewPageWithNameUnderParent();
|
||||
await createNewPageWithNameUnderParent(name: 'Test Document');
|
||||
// tap the editor
|
||||
await tapButton(find.byType(AppFlowyEditor));
|
||||
|
||||
await beforeTest?.call(editor.getCurrentEditorState());
|
||||
|
||||
|
||||
+3
-6
@@ -1,6 +1,4 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -17,12 +15,11 @@ void main() {
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
const pageName = 'Test Document';
|
||||
await tester.createNewPageWithNameUnderParent(name: pageName);
|
||||
|
||||
// expect to see a new document
|
||||
tester.expectToSeePageName(
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
);
|
||||
tester.expectToSeePageName(pageName);
|
||||
// and with one paragraph block
|
||||
expect(find.byType(ParagraphBlockComponentWidget), findsOneWidget);
|
||||
});
|
||||
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('customer:', () {
|
||||
testWidgets('backtick issue - inline code', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
const pageName = 'backtick issue';
|
||||
await tester.createNewPageWithNameUnderParent(name: pageName);
|
||||
|
||||
// focus on the editor
|
||||
await tester.tap(find.byType(AppFlowyEditor));
|
||||
// input backtick
|
||||
const text = '`Hello` AppFlowy';
|
||||
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
await tester.ime.insertCharacter(text[i]);
|
||||
}
|
||||
|
||||
final node = tester.editor.getNodeAtPath([0]);
|
||||
expect(
|
||||
node.delta?.toJson(),
|
||||
equals([
|
||||
{
|
||||
"insert": "Hello",
|
||||
"attributes": {"code": true},
|
||||
},
|
||||
{"insert": " AppFlowy"},
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('backtick issue - inline code', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
const pageName = 'backtick issue';
|
||||
await tester.createNewPageWithNameUnderParent(name: pageName);
|
||||
|
||||
// focus on the editor
|
||||
await tester.tap(find.byType(AppFlowyEditor));
|
||||
// input backtick
|
||||
const text = '```';
|
||||
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
await tester.ime.insertCharacter(text[i]);
|
||||
}
|
||||
|
||||
final node = tester.editor.getNodeAtPath([0]);
|
||||
expect(node.type, equals(CodeBlockKeys.type));
|
||||
});
|
||||
});
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.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('document selection:', () {
|
||||
testWidgets('select text from start to end by pan gesture ',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
final editor = tester.editor;
|
||||
final editorState = editor.getCurrentEditorState();
|
||||
// insert a paragraph
|
||||
final transaction = editorState.transaction;
|
||||
transaction.insertNode(
|
||||
[0],
|
||||
paragraphNode(
|
||||
text:
|
||||
'''Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.''',
|
||||
),
|
||||
);
|
||||
await editorState.apply(transaction);
|
||||
await tester.pumpAndSettle(Durations.short1);
|
||||
|
||||
final textBlocks = find.byType(AppFlowyRichText);
|
||||
final topLeft = tester.getTopLeft(textBlocks.at(0));
|
||||
|
||||
final gesture = await tester.startGesture(
|
||||
topLeft,
|
||||
pointer: 7,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
await gesture.moveBy(const Offset(10, 0));
|
||||
await tester.pump(Durations.short1);
|
||||
}
|
||||
|
||||
expect(editorState.selection!.start.offset, 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('document shortcuts:', () {
|
||||
testWidgets('custom cut command', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
const pageName = 'Test Document Shortcuts';
|
||||
await tester.createNewPageWithNameUnderParent(name: pageName);
|
||||
|
||||
// focus on the editor
|
||||
await tester.tap(find.byType(AppFlowyEditor));
|
||||
|
||||
// mock the data
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final transaction = editorState.transaction;
|
||||
const text1 = '1. First line';
|
||||
const text2 = '2. Second line';
|
||||
transaction.insertNodes([
|
||||
0,
|
||||
], [
|
||||
paragraphNode(text: text1),
|
||||
paragraphNode(text: text2),
|
||||
]);
|
||||
await editorState.apply(transaction);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// focus on the end of the first line
|
||||
await tester.editor.updateSelection(
|
||||
Selection.collapsed(
|
||||
Position(path: [0], offset: text1.length),
|
||||
),
|
||||
);
|
||||
// press the keybinding
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyX,
|
||||
isControlPressed: !UniversalPlatform.isMacOS,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// check the clipboard
|
||||
final clipboard = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
expect(
|
||||
clipboard?.text,
|
||||
equals(text1),
|
||||
);
|
||||
|
||||
final node = tester.editor.getNodeAtPath([0]);
|
||||
expect(
|
||||
node.delta?.toPlainText(),
|
||||
equals(text2),
|
||||
);
|
||||
|
||||
// select the whole line
|
||||
await tester.editor.updateSelection(
|
||||
Selection.single(
|
||||
path: [0],
|
||||
startOffset: 0,
|
||||
endOffset: text2.length,
|
||||
),
|
||||
);
|
||||
|
||||
// press the keybinding
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyX,
|
||||
isControlPressed: !UniversalPlatform.isMacOS,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// all the text should be deleted
|
||||
expect(
|
||||
node.delta?.toPlainText(),
|
||||
equals(''),
|
||||
);
|
||||
|
||||
final clipboard2 = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
expect(
|
||||
clipboard2?.text,
|
||||
equals(text2),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'custom copy command - copy whole line when selection is collapsed',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
const pageName = 'Test Document Shortcuts';
|
||||
await tester.createNewPageWithNameUnderParent(name: pageName);
|
||||
|
||||
// focus on the editor
|
||||
await tester.tap(find.byType(AppFlowyEditor));
|
||||
|
||||
// mock the data
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
final transaction = editorState.transaction;
|
||||
const text1 = '1. First line';
|
||||
transaction.insertNodes([
|
||||
0,
|
||||
], [
|
||||
paragraphNode(text: text1),
|
||||
]);
|
||||
await editorState.apply(transaction);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// focus on the end of the first line
|
||||
await tester.editor.updateSelection(
|
||||
Selection.collapsed(
|
||||
Position(path: [0], offset: text1.length),
|
||||
),
|
||||
);
|
||||
// press the keybinding
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyC,
|
||||
isControlPressed: !UniversalPlatform.isMacOS,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// check the clipboard
|
||||
final clipboard = await Clipboard.getData(Clipboard.kTextPlain);
|
||||
expect(
|
||||
clipboard?.text,
|
||||
equals(text1),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+607
@@ -0,0 +1,607 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/sub_page/sub_page_block_component.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
// Test cases for the Document SubPageBlock that needs to be covered:
|
||||
// - [x] Insert a new SubPageBlock from Slash menu items (Expect it will create a child view under current view)
|
||||
// - [x] Delete a SubPageBlock from Block Action Menu (Expect the view is moved to trash / deleted)
|
||||
// - [x] Delete a SubPageBlock with backspace when selected (Expect the view is moved to trash / deleted)
|
||||
// - [x] Copy+paste a SubPageBlock in same Document (Expect a new view is created under current view with same content and name)
|
||||
// - [x] Copy+paste a SubPageBlock in different Document (Expect a new view is created under current view with same content and name)
|
||||
// - [x] Cut+paste a SubPageBlock in same Document (Expect the view to be deleted on Cut, and brought back on Paste)
|
||||
// - [x] Cut+paste a SubPageBlock in different Document (Expect the view to be deleted on Cut, and brought back on Paste)
|
||||
// - [x] Undo adding a SubPageBlock (Expect the view to be deleted)
|
||||
// - [x] Undo delete of a SubPageBlock (Expect the view to be brought back to original position)
|
||||
// - [x] Redo adding a SubPageBlock (Expect the view to be restored)
|
||||
// - [x] Redo delete of a SubPageBlock (Expect the view to be moved to trash again)
|
||||
// - [x] Renaming a child view (Expect the view name to be updated in the document)
|
||||
// - [x] Deleting a view (to trash) linked to a SubPageBlock shows a hint that the view is in trash (Expect a hint to be shown)
|
||||
// - [x] Deleting a view (in trash) linked to a SubPageBlock deletes the SubPageBlock (Expect the SubPageBlock to be deleted)
|
||||
// - [x] Duplicating a SubPageBlock node from Action Menu (Expect a new view is created under current view with same content and name + (copy))
|
||||
// - [x] Dragging a SubPageBlock node to a new position in the document (Expect everything to be normal)
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Document SubPageBlock tests', () {
|
||||
testWidgets('Insert a new SubPageBlock from Slash menu items',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
expect(
|
||||
find.text(LocaleKeys.menuAppHeader_defaultNewPageName.tr()),
|
||||
findsNWidgets(3),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Rename and then Delete a SubPageBlock from Block Action Menu',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor.hoverAndClickOptionMenuButton([0]);
|
||||
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Copy+paste a SubPageBlock in same Document', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor.hoverAndClickOptionAddButton([0], false);
|
||||
await tester.editor.tapLineOfEditorAt(1);
|
||||
|
||||
// This is a workaround to allow CTRL+A and CTRL+C to work to copy
|
||||
// the SubPageBlock as well.
|
||||
await tester.ime.insertText('ABC');
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyA,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyC,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.editor.hoverAndClickOptionAddButton([1], false);
|
||||
await tester.editor.tapLineOfEditorAt(2);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyV,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle(const Duration(seconds: 5));
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsNWidgets(2));
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
expect(find.text('Child page (copy)'), findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('Copy+paste a SubPageBlock in different Document',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor.hoverAndClickOptionAddButton([0], false);
|
||||
await tester.editor.tapLineOfEditorAt(1);
|
||||
|
||||
// This is a workaround to allow CTRL+A and CTRL+C to work to copy
|
||||
// the SubPageBlock as well.
|
||||
await tester.ime.insertText('ABC');
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyA,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyC,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock-2');
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyV,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock-2',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
expect(find.text('Child page'), findsOneWidget);
|
||||
expect(find.text('Child page (copy)'), findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('Cut+paste a SubPageBlock in same Document', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor
|
||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyX,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsNothing);
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyV,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('Cut+paste a SubPageBlock in different Document',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor
|
||||
.updateSelection(Selection.single(path: [0], startOffset: 0));
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyX,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsNothing);
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock-2');
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyV,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock-2',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
expect(find.text('Child page (copy)'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Undo adding a SubPageBlock', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu(true);
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
// Undo
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyZ,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsNothing);
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Undo delete of a SubPageBlock', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor.hoverAndClickOptionMenuButton([0]);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
expect(find.byType(SubPageBlockComponent), findsNothing);
|
||||
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyZ,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
});
|
||||
|
||||
// Redo: undoing adding a subpage block, then redoing to bring it back
|
||||
// -> Add a subpage block
|
||||
// -> Undo
|
||||
// -> Redo
|
||||
testWidgets('Redo adding of a SubPageBlock', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu(true);
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
// Undo
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyZ,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsNothing);
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
|
||||
// Redo
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyZ,
|
||||
isShiftPressed: true,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
});
|
||||
|
||||
// Redo: undoing deleting a subpage block, then redoing to delete it again
|
||||
// -> Add a subpage block
|
||||
// -> Delete
|
||||
// -> Undo
|
||||
// -> Redo
|
||||
testWidgets('Redo delete of a SubPageBlock', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu(true);
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
// Delete
|
||||
await tester.editor.hoverAndClickOptionMenuButton([1]);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
expect(find.byType(SubPageBlockComponent), findsNothing);
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
|
||||
// Undo
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyZ,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
// Redo
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.keyZ,
|
||||
isShiftPressed: true,
|
||||
isControlPressed: Platform.isLinux || Platform.isWindows,
|
||||
isMetaPressed: Platform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsNothing);
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Delete a view first to trash, then from trash',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
|
||||
final hintText = LocaleKeys.document_plugins_subPage_inTrashHint.tr();
|
||||
expect(find.text(hintText), findsNothing);
|
||||
|
||||
await tester.hoverOnPageName('Child page');
|
||||
await tester.tapDeletePageButton();
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
expect(find.text(hintText), findsOne);
|
||||
|
||||
// Go to trash
|
||||
await tester.tapTrashButton();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap on delete all button
|
||||
await tester.tap(find.text(LocaleKeys.trash_deleteAll.tr()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Tap ok to delete app pages in trash
|
||||
await tester.tap(find.text(LocaleKeys.button_delete.tr()));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.openPage('SubPageBlock');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNothing);
|
||||
expect(find.byType(SubPageBlockComponent), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Duplicate SubPageBlock from Block Menu', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu();
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
await tester
|
||||
.hoverOnPageName(LocaleKeys.menuAppHeader_defaultNewPageName.tr());
|
||||
await tester.renamePage('Child page');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
|
||||
await tester.editor.hoverAndClickOptionMenuButton([0]);
|
||||
|
||||
await tester.tapButtonWithName(LocaleKeys.button_duplicate.tr());
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Child page'), findsNWidgets(2));
|
||||
expect(find.text('Child page (copy)'), findsNWidgets(2));
|
||||
expect(find.byType(SubPageBlockComponent), findsNWidgets(2));
|
||||
});
|
||||
|
||||
testWidgets('Drag SubPageBlock to top of Document', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
await tester.createNewPageWithNameUnderParent(name: 'SubPageBlock');
|
||||
|
||||
await tester.insertSubPageFromSlashMenu(true);
|
||||
|
||||
await tester.expandOrCollapsePage(
|
||||
pageName: 'SubPageBlock',
|
||||
layout: ViewLayoutPB.Document,
|
||||
);
|
||||
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
|
||||
final beforeNode = tester.editor.getNodeAtPath([1]);
|
||||
|
||||
await tester.editor.dragBlock([1], const Offset(20, -45));
|
||||
await tester.pumpAndSettle(Durations.long1);
|
||||
|
||||
final afterNode = tester.editor.getNodeAtPath([0]);
|
||||
|
||||
expect(afterNode.type, SubPageBlockKeys.type);
|
||||
expect(afterNode.type, beforeNode.type);
|
||||
expect(find.byType(SubPageBlockComponent), findsOneWidget);
|
||||
expect(
|
||||
find.text(LocaleKeys.document_plugins_subPage_inTrashHint.tr()),
|
||||
findsNothing,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
extension _SubPageTestHelper on WidgetTester {
|
||||
Future<void> insertSubPageFromSlashMenu([bool withTextNode = false]) async {
|
||||
await editor.tapLineOfEditorAt(0);
|
||||
|
||||
if (withTextNode) {
|
||||
await ime.insertText('ABC');
|
||||
await editor.getCurrentEditorState().insertNewLine();
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
await editor.showSlashMenu();
|
||||
await editor.tapSlashMenuItemWithName(
|
||||
LocaleKeys.document_slashMenu_subPage_name.tr(),
|
||||
offset: 100,
|
||||
);
|
||||
|
||||
await pumpUntilFound(find.byType(SubPageBlockComponent));
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -5,10 +5,11 @@ import 'document_codeblock_paste_test.dart' as document_codeblock_paste_test;
|
||||
import 'document_copy_and_paste_test.dart' as document_copy_and_paste_test;
|
||||
import 'document_create_and_delete_test.dart'
|
||||
as document_create_and_delete_test;
|
||||
import 'document_option_action_test.dart' as document_option_action_test;
|
||||
import 'document_inline_page_reference_test.dart'
|
||||
as document_inline_page_reference_test;
|
||||
import 'document_more_actions_test.dart' as document_more_actions_test;
|
||||
import 'document_option_action_test.dart' as document_option_action_test;
|
||||
import 'document_shortcuts_test.dart' as document_shortcuts_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;
|
||||
@@ -45,4 +46,5 @@ void startTesting() {
|
||||
document_inline_page_reference_test.main();
|
||||
document_more_actions_test.main();
|
||||
document_with_file_test.main();
|
||||
document_shortcuts_test.main();
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.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:universal_platform/universal_platform.dart';
|
||||
|
||||
import '../../shared/constants.dart';
|
||||
import '../../shared/util.dart';
|
||||
|
||||
const _testDocumentName = 'Test Document';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('document title:', () {
|
||||
testWidgets('create a new document and edit title', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
final title = tester.editor.findDocumentTitle('');
|
||||
expect(title, findsOneWidget);
|
||||
|
||||
// input name
|
||||
await tester.enterText(title, _testDocumentName);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final newTitle = tester.editor.findDocumentTitle(_testDocumentName);
|
||||
expect(newTitle, findsOneWidget);
|
||||
|
||||
// press enter to create a new line
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.enter);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const firstLine = 'First line of text';
|
||||
await tester.ime.insertText(firstLine);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final firstLineText = find.text(firstLine, findRichText: true);
|
||||
expect(firstLineText, findsOneWidget);
|
||||
|
||||
// press cmd/ctrl+left to move the cursor to the start of the line
|
||||
if (UniversalPlatform.isMacOS) {
|
||||
await tester.simulateKeyEvent(
|
||||
LogicalKeyboardKey.arrowLeft,
|
||||
isMetaPressed: true,
|
||||
);
|
||||
} else {
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.home);
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// press arrow left to delete the first line
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// check if the title is on focus
|
||||
final titleOnFocus = tester.editor.findDocumentTitle(_testDocumentName);
|
||||
final titleWidget = tester.widget<TextField>(titleOnFocus);
|
||||
expect(titleWidget.focusNode?.hasFocus, isTrue);
|
||||
|
||||
// press the right arrow key to move the cursor to the first line
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
|
||||
// check if the title is not on focus
|
||||
expect(titleWidget.focusNode?.hasFocus, isFalse);
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
expect(editorState.selection, Selection.collapsed(Position(path: [0])));
|
||||
|
||||
// press the backspace key to go to the title
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.backspace);
|
||||
|
||||
expect(editorState.selection, null);
|
||||
expect(titleWidget.focusNode?.hasFocus, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('check if the title is saved', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
final title = tester.editor.findDocumentTitle('');
|
||||
expect(title, findsOneWidget);
|
||||
|
||||
// input name
|
||||
await tester.enterText(title, _testDocumentName);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
if (UniversalPlatform.isLinux) {
|
||||
// wait for the name to be saved
|
||||
await tester.wait(250);
|
||||
}
|
||||
|
||||
// go to the get started page
|
||||
await tester.tapButton(
|
||||
tester.findPageName(Constants.gettingStartedPageName),
|
||||
);
|
||||
|
||||
// go back to the page
|
||||
await tester.tapButton(tester.findPageName(_testDocumentName));
|
||||
|
||||
// check if the title is saved
|
||||
final testDocumentTitle = tester.editor.findDocumentTitle(
|
||||
_testDocumentName,
|
||||
);
|
||||
expect(testDocumentTitle, findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('arrow up from first line moves focus to title',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
final title = tester.editor.findDocumentTitle('');
|
||||
await tester.enterText(title, _testDocumentName);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.enter);
|
||||
await tester.ime.insertText('First line of text');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.home);
|
||||
|
||||
// press the arrow upload
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.arrowUp);
|
||||
|
||||
final titleWidget = tester.widget<TextField>(
|
||||
tester.editor.findDocumentTitle(_testDocumentName),
|
||||
);
|
||||
expect(titleWidget.focusNode?.hasFocus, isTrue);
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
expect(editorState.selection, null);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'backspace at start of first line moves focus to title and deletes empty paragraph',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
final title = tester.editor.findDocumentTitle('');
|
||||
await tester.enterText(title, _testDocumentName);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.enter);
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
expect(editorState.document.root.children.length, equals(2));
|
||||
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.backspace);
|
||||
|
||||
final titleWidget = tester.widget<TextField>(
|
||||
tester.editor.findDocumentTitle(_testDocumentName),
|
||||
);
|
||||
expect(titleWidget.focusNode?.hasFocus, isTrue);
|
||||
|
||||
// at least one empty paragraph node is created
|
||||
expect(editorState.document.root.children.length, equals(1));
|
||||
});
|
||||
|
||||
testWidgets('arrow right from end of title moves focus to first line',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
final title = tester.editor.findDocumentTitle('');
|
||||
await tester.enterText(title, _testDocumentName);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.enter);
|
||||
await tester.ime.insertText('First line of text');
|
||||
|
||||
await tester.tapButton(
|
||||
tester.editor.findDocumentTitle(_testDocumentName),
|
||||
);
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.end);
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.arrowRight);
|
||||
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
expect(
|
||||
editorState.selection,
|
||||
Selection.collapsed(
|
||||
Position(path: [0]),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('change the title via sidebar, check the title is updated',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
final title = tester.editor.findDocumentTitle('');
|
||||
expect(title, findsOneWidget);
|
||||
|
||||
await tester.hoverOnPageName(
|
||||
'',
|
||||
onHover: () async {
|
||||
await tester.renamePage(_testDocumentName);
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final newTitle = tester.editor.findDocumentTitle(_testDocumentName);
|
||||
expect(newTitle, findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('press arrow down key in title, check if the cursor flashes',
|
||||
(tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent();
|
||||
|
||||
final title = tester.editor.findDocumentTitle('');
|
||||
await tester.enterText(title, _testDocumentName);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.enter);
|
||||
const inputText = 'Hello World';
|
||||
await tester.ime.insertText(inputText);
|
||||
|
||||
await tester.tapButton(
|
||||
tester.editor.findDocumentTitle(_testDocumentName),
|
||||
);
|
||||
await tester.simulateKeyEvent(LogicalKeyboardKey.arrowDown);
|
||||
final editorState = tester.editor.getCurrentEditorState();
|
||||
expect(
|
||||
editorState.selection,
|
||||
Selection.collapsed(
|
||||
Position(path: [0], offset: inputText.length),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
+3
-2
@@ -1,8 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_header_node_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
+51
@@ -60,6 +60,57 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('insert multiple referenced boards', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
// create a new grid
|
||||
final id = uuid();
|
||||
final name = '${ViewLayoutPB.Board.name}_$id';
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: name,
|
||||
layout: ViewLayoutPB.Board,
|
||||
openAfterCreated: false,
|
||||
);
|
||||
// create a new document
|
||||
await tester.createNewPageWithNameUnderParent(
|
||||
name: 'insert_a_reference_${ViewLayoutPB.Board.name}',
|
||||
);
|
||||
// tap the first line of the document
|
||||
await tester.editor.tapLineOfEditorAt(0);
|
||||
// insert a referenced view
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName(
|
||||
ViewLayoutPB.Board.slashMenuLinkedName,
|
||||
);
|
||||
final referencedDatabase1 = find.descendant(
|
||||
of: find.byType(InlineActionsHandler),
|
||||
matching: find.findTextInFlowyText(name),
|
||||
);
|
||||
expect(referencedDatabase1, findsOneWidget);
|
||||
await tester.tapButton(referencedDatabase1);
|
||||
|
||||
await tester.editor.tapLineOfEditorAt(1);
|
||||
await tester.editor.showSlashMenu();
|
||||
await tester.editor.tapSlashMenuItemWithName(
|
||||
ViewLayoutPB.Board.slashMenuLinkedName,
|
||||
);
|
||||
final referencedDatabase2 = find.descendant(
|
||||
of: find.byType(InlineActionsHandler),
|
||||
matching: find.findTextInFlowyText(name),
|
||||
);
|
||||
expect(referencedDatabase2, findsOneWidget);
|
||||
await tester.tapButton(referencedDatabase2);
|
||||
|
||||
expect(
|
||||
find.descendant(
|
||||
of: find.byType(AppFlowyEditor),
|
||||
matching: find.byType(DesktopBoardPage),
|
||||
),
|
||||
findsNWidgets(2),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('insert a referenced calendar', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
+2
-8
@@ -1,5 +1,7 @@
|
||||
import 'dart:io';
|
||||
|
||||
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';
|
||||
@@ -8,7 +10,6 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.da
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
@@ -36,10 +37,6 @@ void main() {
|
||||
LocaleKeys.document_slashMenu_name_file.tr(),
|
||||
);
|
||||
expect(find.byType(FileBlockComponent), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byType(FileBlockComponent));
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
expect(find.byType(FileUploadMenu), findsOneWidget);
|
||||
|
||||
final image = await rootBundle.load('assets/test/images/sample.jpeg');
|
||||
@@ -116,9 +113,6 @@ void main() {
|
||||
LocaleKeys.document_slashMenu_name_file.tr(),
|
||||
);
|
||||
expect(find.byType(FileBlockComponent), findsOneWidget);
|
||||
|
||||
await tester.tap(find.byType(FileBlockComponent));
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
expect(find.byType(FileUploadMenu), findsOneWidget);
|
||||
|
||||
// Navigate to integrate link tab
|
||||
|
||||
+2
-3
@@ -161,9 +161,8 @@ void main() {
|
||||
);
|
||||
expect(find.byType(UploadImageMenu), findsOneWidget);
|
||||
|
||||
await tester.tapButtonWithName(
|
||||
'Unsplash',
|
||||
);
|
||||
await tester.tapButtonWithName('Unsplash');
|
||||
await tester.pumpUntilFound(find.byType(UnsplashImageWidget));
|
||||
expect(find.byType(UnsplashImageWidget), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:time/time.dart';
|
||||
|
||||
import '../../shared/database_test_op.dart';
|
||||
import 'grid_test_extensions.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid edit row test:', () {
|
||||
testWidgets('with sort configured', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final unsorted = tester.getGridRows();
|
||||
|
||||
// add a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
|
||||
|
||||
final sorted = [
|
||||
unsorted[7],
|
||||
unsorted[8],
|
||||
unsorted[1],
|
||||
unsorted[9],
|
||||
unsorted[11],
|
||||
unsorted[10],
|
||||
unsorted[6],
|
||||
unsorted[12],
|
||||
unsorted[2],
|
||||
unsorted[0],
|
||||
unsorted[3],
|
||||
unsorted[5],
|
||||
unsorted[4],
|
||||
];
|
||||
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(sorted));
|
||||
|
||||
await tester.editCell(
|
||||
rowIndex: 4,
|
||||
fieldType: FieldType.RichText,
|
||||
input: "x",
|
||||
);
|
||||
await tester.pumpAndSettle(200.milliseconds);
|
||||
|
||||
final reSorted = [
|
||||
unsorted[7],
|
||||
unsorted[8],
|
||||
unsorted[1],
|
||||
unsorted[9],
|
||||
unsorted[10],
|
||||
unsorted[6],
|
||||
unsorted[12],
|
||||
unsorted[2],
|
||||
unsorted[0],
|
||||
unsorted[3],
|
||||
unsorted[5],
|
||||
unsorted[11],
|
||||
unsorted[4],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(reSorted));
|
||||
|
||||
// delete the sort
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapDeleteAllSortsButton();
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(unsorted));
|
||||
});
|
||||
|
||||
testWidgets('with filter configured', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final original = tester.getGridRows();
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(
|
||||
FieldType.Checkbox,
|
||||
'Registration Complete',
|
||||
);
|
||||
|
||||
final filtered = [
|
||||
original[1],
|
||||
original[3],
|
||||
original[5],
|
||||
original[6],
|
||||
original[7],
|
||||
original[9],
|
||||
original[12],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filtered));
|
||||
expect(actual.length, equals(7));
|
||||
tester.assertNumberOfRowsInGridPage(7);
|
||||
|
||||
await tester.tapCheckboxCellInGrid(rowIndex: 0);
|
||||
await tester.pumpAndSettle(200.milliseconds);
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual.length, equals(6));
|
||||
tester.assertNumberOfRowsInGridPage(6);
|
||||
final edited = [
|
||||
original[3],
|
||||
original[5],
|
||||
original[6],
|
||||
original[7],
|
||||
original[9],
|
||||
original[12],
|
||||
];
|
||||
expect(actual, orderedEquals(edited));
|
||||
|
||||
// delete the filter
|
||||
await tester.tapFilterButtonInGrid('Registration Complete');
|
||||
await tester
|
||||
.tapDisclosureButtonInFinder(find.byType(CheckboxFilterEditor));
|
||||
await tester.tapDeleteFilterButtonInGrid();
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual.length, equals(13));
|
||||
tester.assertNumberOfRowsInGridPage(13);
|
||||
});
|
||||
});
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/database_test_op.dart';
|
||||
import 'grid_test_extensions.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid simultaneous sort and filter test:', () {
|
||||
// testWidgets('delete filter with active sort', (tester) async {
|
||||
// await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// // get grid data
|
||||
// final original = tester.getGridRows();
|
||||
|
||||
// // add a filter
|
||||
// await tester.tapDatabaseFilterButton();
|
||||
// await tester.tapCreateFilterByFieldType(
|
||||
// FieldType.Checkbox,
|
||||
// 'Registration Complete',
|
||||
// );
|
||||
|
||||
// // add a sort
|
||||
// await tester.tapDatabaseSortButton();
|
||||
// await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
|
||||
|
||||
// final filteredAndSorted = [
|
||||
// original[7],
|
||||
// original[1],
|
||||
// original[9],
|
||||
// original[6],
|
||||
// original[12],
|
||||
// original[3],
|
||||
// original[5],
|
||||
// ];
|
||||
|
||||
// // verify grid data
|
||||
// List actual = tester.getGridRows();
|
||||
// expect(actual, orderedEquals(filteredAndSorted));
|
||||
|
||||
// // delete the filter
|
||||
// await tester.tapFilterButtonInGrid('Registration Complete');
|
||||
// await tester
|
||||
// .tapDisclosureButtonInFinder(find.byType(CheckboxFilterEditor));
|
||||
// await tester.tapDeleteFilterButtonInGrid();
|
||||
|
||||
// final sorted = [
|
||||
// original[7],
|
||||
// original[8],
|
||||
// original[1],
|
||||
// original[9],
|
||||
// original[11],
|
||||
// original[10],
|
||||
// original[6],
|
||||
// original[12],
|
||||
// original[2],
|
||||
// original[0],
|
||||
// original[3],
|
||||
// original[5],
|
||||
// original[4],
|
||||
// ];
|
||||
|
||||
// // verify grid data
|
||||
// actual = tester.getGridRows();
|
||||
// expect(actual, orderedEquals(sorted));
|
||||
// });
|
||||
|
||||
testWidgets('delete sort with active fiilter', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final original = tester.getGridRows();
|
||||
|
||||
// add a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(
|
||||
FieldType.Checkbox,
|
||||
'Registration Complete',
|
||||
);
|
||||
|
||||
// add a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
|
||||
|
||||
final filteredAndSorted = [
|
||||
original[7],
|
||||
original[1],
|
||||
original[9],
|
||||
original[6],
|
||||
original[12],
|
||||
original[3],
|
||||
original[5],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filteredAndSorted));
|
||||
|
||||
// delete the sort
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapDeleteAllSortsButton();
|
||||
|
||||
final filtered = [
|
||||
original[1],
|
||||
original[3],
|
||||
original[5],
|
||||
original[6],
|
||||
original[7],
|
||||
original[9],
|
||||
original[12],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filtered));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/database_test_op.dart';
|
||||
import '../../shared/util.dart';
|
||||
import 'grid_test_extensions.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid reopen test:', () {
|
||||
testWidgets('base case', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
final expected = tester.getGridRows();
|
||||
|
||||
// go to another page and come back
|
||||
await tester.openPage('Getting started');
|
||||
await tester.openPage('v069', layout: ViewLayoutPB.Grid);
|
||||
|
||||
// verify grid data
|
||||
final actual = tester.getGridRows();
|
||||
|
||||
expect(actual, orderedEquals(expected));
|
||||
});
|
||||
|
||||
testWidgets('with sort configured', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final unsorted = tester.getGridRows();
|
||||
|
||||
// add a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
|
||||
|
||||
final sorted = [
|
||||
unsorted[7],
|
||||
unsorted[8],
|
||||
unsorted[1],
|
||||
unsorted[9],
|
||||
unsorted[11],
|
||||
unsorted[10],
|
||||
unsorted[6],
|
||||
unsorted[12],
|
||||
unsorted[2],
|
||||
unsorted[0],
|
||||
unsorted[3],
|
||||
unsorted[5],
|
||||
unsorted[4],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(sorted));
|
||||
|
||||
// go to another page and come back
|
||||
await tester.openPage('Getting started');
|
||||
await tester.openPage('v069', layout: ViewLayoutPB.Grid);
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(sorted));
|
||||
|
||||
// delete sorts
|
||||
// TODO(RS): Shouldn't the sort/filter list show automatically!?
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapSortMenuInSettingBar();
|
||||
await tester.tapDeleteAllSortsButton();
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(unsorted));
|
||||
|
||||
// go to another page and come back
|
||||
await tester.openPage('Getting started');
|
||||
await tester.openPage('v069', layout: ViewLayoutPB.Grid);
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(unsorted));
|
||||
});
|
||||
|
||||
testWidgets('with filter configured', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final unfiltered = tester.getGridRows();
|
||||
|
||||
// add a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(
|
||||
FieldType.Checkbox,
|
||||
'Registration Complete',
|
||||
);
|
||||
|
||||
final filtered = [
|
||||
unfiltered[1],
|
||||
unfiltered[3],
|
||||
unfiltered[5],
|
||||
unfiltered[6],
|
||||
unfiltered[7],
|
||||
unfiltered[9],
|
||||
unfiltered[12],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filtered));
|
||||
|
||||
// go to another page and come back
|
||||
await tester.openPage('Getting started');
|
||||
await tester.openPage('v069', layout: ViewLayoutPB.Grid);
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filtered));
|
||||
|
||||
// delete the filter
|
||||
// TODO(RS): Shouldn't the sort/filter list show automatically!?
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapFilterButtonInGrid('Registration Complete');
|
||||
await tester
|
||||
.tapDisclosureButtonInFinder(find.byType(CheckboxFilterEditor));
|
||||
await tester.tapDeleteFilterButtonInGrid();
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(unfiltered));
|
||||
|
||||
// go to another page and come back
|
||||
await tester.openPage('Getting started');
|
||||
await tester.openPage('v069', layout: ViewLayoutPB.Grid);
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(unfiltered));
|
||||
});
|
||||
|
||||
testWidgets('with both filter and sort configured', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final original = tester.getGridRows();
|
||||
|
||||
// add a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(
|
||||
FieldType.Checkbox,
|
||||
'Registration Complete',
|
||||
);
|
||||
|
||||
// add a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
|
||||
|
||||
final filteredAndSorted = [
|
||||
original[7],
|
||||
original[1],
|
||||
original[9],
|
||||
original[6],
|
||||
original[12],
|
||||
original[3],
|
||||
original[5],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filteredAndSorted));
|
||||
|
||||
// go to another page and come back
|
||||
await tester.openPage('Getting started');
|
||||
await tester.openPage('v069', layout: ViewLayoutPB.Grid);
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filteredAndSorted));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.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';
|
||||
import 'grid_test_extensions.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid reorder row test:', () {
|
||||
testWidgets('base case', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final original = tester.getGridRows();
|
||||
|
||||
// reorder row
|
||||
await tester.reorderRow(original[4], original[1]);
|
||||
|
||||
// verify grid data
|
||||
List reordered = [
|
||||
original[0],
|
||||
original[4],
|
||||
original[1],
|
||||
original[2],
|
||||
original[3],
|
||||
original[5],
|
||||
original[6],
|
||||
original[7],
|
||||
original[8],
|
||||
original[9],
|
||||
original[10],
|
||||
original[11],
|
||||
original[12],
|
||||
];
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(reordered));
|
||||
|
||||
// reorder row
|
||||
await tester.reorderRow(reordered[1], reordered[3]);
|
||||
|
||||
// verify grid data
|
||||
reordered = [
|
||||
original[0],
|
||||
original[1],
|
||||
original[2],
|
||||
original[4],
|
||||
original[3],
|
||||
original[5],
|
||||
original[6],
|
||||
original[7],
|
||||
original[8],
|
||||
original[9],
|
||||
original[10],
|
||||
original[11],
|
||||
original[12],
|
||||
];
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(reordered));
|
||||
|
||||
// reorder row
|
||||
await tester.reorderRow(reordered[2], reordered[0]);
|
||||
|
||||
// verify grid data
|
||||
reordered = [
|
||||
original[2],
|
||||
original[0],
|
||||
original[1],
|
||||
original[4],
|
||||
original[3],
|
||||
original[5],
|
||||
original[6],
|
||||
original[7],
|
||||
original[8],
|
||||
original[9],
|
||||
original[10],
|
||||
original[11],
|
||||
original[12],
|
||||
];
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(reordered));
|
||||
});
|
||||
|
||||
testWidgets('with active sort', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final original = tester.getGridRows();
|
||||
|
||||
// add a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
|
||||
|
||||
// verify grid data
|
||||
final sorted = [
|
||||
original[7],
|
||||
original[8],
|
||||
original[1],
|
||||
original[9],
|
||||
original[11],
|
||||
original[10],
|
||||
original[6],
|
||||
original[12],
|
||||
original[2],
|
||||
original[0],
|
||||
original[3],
|
||||
original[5],
|
||||
original[4],
|
||||
];
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(sorted));
|
||||
|
||||
// reorder row
|
||||
await tester.reorderRow(original[4], original[1]);
|
||||
expect(find.byType(ConfirmPopup), findsOneWidget);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_cancel.tr());
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(sorted));
|
||||
});
|
||||
|
||||
testWidgets('with active filter', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final original = tester.getGridRows();
|
||||
|
||||
// add a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(
|
||||
FieldType.Checkbox,
|
||||
'Registration Complete',
|
||||
);
|
||||
|
||||
final filtered = [
|
||||
original[1],
|
||||
original[3],
|
||||
original[5],
|
||||
original[6],
|
||||
original[7],
|
||||
original[9],
|
||||
original[12],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filtered));
|
||||
|
||||
// reorder row
|
||||
await tester.reorderRow(filtered[3], filtered[1]);
|
||||
|
||||
// verify grid data
|
||||
List reordered = [
|
||||
original[1],
|
||||
original[6],
|
||||
original[3],
|
||||
original[5],
|
||||
original[7],
|
||||
original[9],
|
||||
original[12],
|
||||
];
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(reordered));
|
||||
|
||||
// reorder row
|
||||
await tester.reorderRow(reordered[3], reordered[5]);
|
||||
|
||||
// verify grid data
|
||||
reordered = [
|
||||
original[1],
|
||||
original[6],
|
||||
original[3],
|
||||
original[7],
|
||||
original[9],
|
||||
original[5],
|
||||
original[12],
|
||||
];
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(reordered));
|
||||
|
||||
// delete the filter
|
||||
await tester.tapFilterButtonInGrid('Registration Complete');
|
||||
await tester
|
||||
.tapDisclosureButtonInFinder(find.byType(CheckboxFilterEditor));
|
||||
await tester.tapDeleteFilterButtonInGrid();
|
||||
|
||||
// verify grid data
|
||||
final expected = [
|
||||
original[0],
|
||||
original[1],
|
||||
original[2],
|
||||
original[6],
|
||||
original[3],
|
||||
original[4],
|
||||
original[7],
|
||||
original[8],
|
||||
original[9],
|
||||
original[5],
|
||||
original[10],
|
||||
original[11],
|
||||
original[12],
|
||||
];
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(expected));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:easy_localization/easy_localization.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';
|
||||
|
||||
import 'grid_test_extensions.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('grid row test:', () {
|
||||
testWidgets('create from the bottom', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
final expected = tester.getGridRows();
|
||||
|
||||
// create row
|
||||
await tester.tapCreateRowButtonInGrid();
|
||||
|
||||
final actual = tester.getGridRows();
|
||||
expect(actual.slice(0, 3), orderedEquals(expected));
|
||||
expect(actual.length, equals(4));
|
||||
tester.assertNumberOfRowsInGridPage(4);
|
||||
});
|
||||
|
||||
testWidgets('create from a row\'s menu', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
|
||||
final expected = tester.getGridRows();
|
||||
|
||||
// create row
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
await tester.tapCreateRowButtonAfterHoveringOnGridRow();
|
||||
|
||||
final actual = tester.getGridRows();
|
||||
expect([actual[0], actual[2], actual[3]], orderedEquals(expected));
|
||||
expect(actual.length, equals(4));
|
||||
tester.assertNumberOfRowsInGridPage(4);
|
||||
});
|
||||
|
||||
testWidgets('create with sort configured', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final unsorted = tester.getGridRows();
|
||||
|
||||
// add a sort
|
||||
await tester.tapDatabaseSortButton();
|
||||
await tester.tapCreateSortByFieldType(FieldType.RichText, 'Name');
|
||||
|
||||
final sorted = [
|
||||
unsorted[7],
|
||||
unsorted[8],
|
||||
unsorted[1],
|
||||
unsorted[9],
|
||||
unsorted[11],
|
||||
unsorted[10],
|
||||
unsorted[6],
|
||||
unsorted[12],
|
||||
unsorted[2],
|
||||
unsorted[0],
|
||||
unsorted[3],
|
||||
unsorted[5],
|
||||
unsorted[4],
|
||||
];
|
||||
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(sorted));
|
||||
|
||||
// create row
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
await tester.tapCreateRowButtonAfterHoveringOnGridRow();
|
||||
|
||||
// cancel
|
||||
expect(find.byType(ConfirmPopup), findsOneWidget);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_cancel.tr());
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(sorted));
|
||||
|
||||
// try again, but confirm this time
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
await tester.tapCreateRowButtonAfterHoveringOnGridRow();
|
||||
expect(find.byType(ConfirmPopup), findsOneWidget);
|
||||
await tester.tapButtonWithName(LocaleKeys.button_remove.tr());
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual.length, equals(14));
|
||||
tester.assertNumberOfRowsInGridPage(14);
|
||||
});
|
||||
|
||||
testWidgets('create with filter configured', (tester) async {
|
||||
await tester.openTestDatabase(v069GridFileName);
|
||||
|
||||
// get grid data
|
||||
final original = tester.getGridRows();
|
||||
|
||||
// create a filter
|
||||
await tester.tapDatabaseFilterButton();
|
||||
await tester.tapCreateFilterByFieldType(
|
||||
FieldType.Checkbox,
|
||||
'Registration Complete',
|
||||
);
|
||||
|
||||
final filtered = [
|
||||
original[1],
|
||||
original[3],
|
||||
original[5],
|
||||
original[6],
|
||||
original[7],
|
||||
original[9],
|
||||
original[12],
|
||||
];
|
||||
|
||||
// verify grid data
|
||||
List actual = tester.getGridRows();
|
||||
expect(actual, orderedEquals(filtered));
|
||||
|
||||
// create row (one before and after the first row, and one at the bottom)
|
||||
await tester.tapCreateRowButtonInGrid();
|
||||
await tester.hoverOnFirstRowOfGrid();
|
||||
await tester.tapCreateRowButtonAfterHoveringOnGridRow();
|
||||
await tester.hoverOnFirstRowOfGrid(() async {
|
||||
await tester.tapRowMenuButtonInGrid();
|
||||
await tester.tapCreateRowAboveButtonInRowMenu();
|
||||
});
|
||||
|
||||
actual = tester.getGridRows();
|
||||
expect(actual.length, equals(10));
|
||||
tester.assertNumberOfRowsInGridPage(10);
|
||||
actual = [
|
||||
actual[1],
|
||||
actual[3],
|
||||
actual[4],
|
||||
actual[5],
|
||||
actual[6],
|
||||
actual[7],
|
||||
actual[8],
|
||||
];
|
||||
expect(actual, orderedEquals(filtered));
|
||||
|
||||
// delete the filter
|
||||
await tester.tapFilterButtonInGrid('Registration Complete');
|
||||
await tester
|
||||
.tapDisclosureButtonInFinder(find.byType(CheckboxFilterEditor));
|
||||
await tester.tapDeleteFilterButtonInGrid();
|
||||
|
||||
// verify grid data
|
||||
actual = tester.getGridRows();
|
||||
expect(actual.length, equals(16));
|
||||
tester.assertNumberOfRowsInGridPage(16);
|
||||
actual = [
|
||||
actual[0],
|
||||
actual[2],
|
||||
actual[4],
|
||||
actual[5],
|
||||
actual[6],
|
||||
actual[7],
|
||||
actual[8],
|
||||
actual[9],
|
||||
actual[10],
|
||||
actual[11],
|
||||
actual[12],
|
||||
actual[13],
|
||||
actual[14],
|
||||
];
|
||||
expect(actual, orderedEquals(original));
|
||||
});
|
||||
|
||||
testWidgets('delete row of the grid', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
|
||||
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();
|
||||
|
||||
// 3 initial rows - 1 deleted
|
||||
tester.assertNumberOfRowsInGridPage(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
extension GridTestExtensions on WidgetTester {
|
||||
List<RowId> getGridRows() {
|
||||
final databaseController =
|
||||
widget<GridPage>(find.byType(GridPage)).databaseController;
|
||||
return [
|
||||
...databaseController.rowCache.rowInfos.map((e) => e.rowId),
|
||||
];
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@ import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import '../../shared/base.dart';
|
||||
import '../../shared/common_operations.dart';
|
||||
import '../../shared/editor_test_operations.dart';
|
||||
import '../../shared/document_test_operations.dart';
|
||||
import '../../shared/expectation.dart';
|
||||
import '../../shared/keyboard.dart';
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:integration_test/integration_test.dart';
|
||||
import 'notifications_settings_test.dart' as notifications_settings_test;
|
||||
import 'settings_billing_test.dart' as settings_billing_test;
|
||||
import 'shortcuts_settings_test.dart' as shortcuts_settings_test;
|
||||
import 'sign_in_page_settings_test.dart' as sign_in_page_settings_test;
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
@@ -10,4 +11,5 @@ void main() {
|
||||
notifications_settings_test.main();
|
||||
settings_billing_test.main();
|
||||
shortcuts_settings_test.main();
|
||||
sign_in_page_settings_test.main();
|
||||
}
|
||||
+9
-6
@@ -1,11 +1,10 @@
|
||||
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/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -16,8 +15,8 @@ import '../board/board_hide_groups_test.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('shortcuts test', () {
|
||||
testWidgets('can change and overwrite shortcut', (tester) async {
|
||||
group('shortcuts:', () {
|
||||
testWidgets('change and overwrite shortcut', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
@@ -29,7 +28,11 @@ void main() {
|
||||
LocaleKeys.settings_shortcutsPage_keybindings_backspace.tr();
|
||||
|
||||
// Input "Delete" into the search field
|
||||
await tester.enterText(find.byType(TextField), backspaceCmd);
|
||||
final inputField = find.descendant(
|
||||
of: find.byType(SettingsShortcutsView),
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await tester.enterText(inputField, backspaceCmd);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.hoverOnWidget(
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
import 'package:appflowy/env/cloud_env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:toastification/toastification.dart';
|
||||
|
||||
import '../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
Finder findServerType(AuthenticatorType type) {
|
||||
return find
|
||||
.descendant(
|
||||
of: find.byType(SettingsServerDropdownMenu),
|
||||
matching: find.findTextInFlowyText(
|
||||
type.label,
|
||||
),
|
||||
)
|
||||
.last;
|
||||
}
|
||||
|
||||
group('sign-in page settings: ', () {
|
||||
testWidgets('change server type', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
|
||||
// reset the app to the default state
|
||||
await useAppFlowyBetaCloudWithURL(
|
||||
kAppflowyCloudUrl,
|
||||
AuthenticatorType.appflowyCloud,
|
||||
);
|
||||
|
||||
// open the settings page
|
||||
final settingsButton = find.byType(DesktopSignInSettingsButton);
|
||||
await tester.tapButton(settingsButton);
|
||||
|
||||
expect(find.byType(SimpleSettingsDialog), findsOneWidget);
|
||||
|
||||
// the default type should be appflowy cloud
|
||||
final appflowyCloudType = findServerType(AuthenticatorType.appflowyCloud);
|
||||
expect(appflowyCloudType, findsOneWidget);
|
||||
|
||||
// change the server type to self-host
|
||||
await tester.tapButton(appflowyCloudType);
|
||||
final selfhostedButton = findServerType(
|
||||
AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapButton(selfhostedButton);
|
||||
|
||||
// update server url
|
||||
const serverUrl = 'https://test.appflowy.cloud';
|
||||
await tester.enterText(
|
||||
find.byKey(kSelfHostedTextInputFieldKey),
|
||||
serverUrl,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tapButton(
|
||||
find.findTextInFlowyText(LocaleKeys.button_save.tr()),
|
||||
);
|
||||
|
||||
// wait the app to restart, and the tooltip to disappear
|
||||
await tester.pumpUntilNotFound(find.byType(BuiltInToastBuilder));
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 250));
|
||||
|
||||
// open settings page to check the result
|
||||
await tester.tapButton(settingsButton);
|
||||
|
||||
// check the server type
|
||||
expect(
|
||||
findServerType(AuthenticatorType.appflowyCloudSelfHost),
|
||||
findsOneWidget,
|
||||
);
|
||||
// check the server url
|
||||
expect(
|
||||
find.text(serverUrl),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// reset to appflowy cloud
|
||||
await tester.tapButton(
|
||||
findServerType(AuthenticatorType.appflowyCloudSelfHost),
|
||||
);
|
||||
// change the server type to appflowy cloud
|
||||
await tester.tapButton(
|
||||
findServerType(AuthenticatorType.appflowyCloud),
|
||||
);
|
||||
|
||||
// wait the app to restart, and the tooltip to disappear
|
||||
await tester.pumpUntilNotFound(find.byType(BuiltInToastBuilder));
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 250));
|
||||
|
||||
// check the server type
|
||||
await tester.tapButton(settingsButton);
|
||||
expect(
|
||||
findServerType(AuthenticatorType.appflowyCloud),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/board/presentation/board_page.dart';
|
||||
import 'package:appflowy/plugins/database/calendar/presentation/calendar_page.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
|
||||
@@ -8,7 +7,6 @@ import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_button.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
@@ -17,7 +15,7 @@ import '../../shared/util.dart';
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('sidebar test', () {
|
||||
group('sidebar:', () {
|
||||
testWidgets('create a new page', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
@@ -26,9 +24,7 @@ void main() {
|
||||
await tester.tapNewPageButton();
|
||||
|
||||
// expect to see a new document
|
||||
tester.expectToSeePageName(
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
);
|
||||
tester.expectToSeePageName('');
|
||||
// and with one paragraph block
|
||||
expect(find.byType(ParagraphBlockComponentWidget), findsOneWidget);
|
||||
});
|
||||
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
import 'package:appflowy/startup/tasks/app_window_size_manager.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/hotkeys.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
|
||||
import '../../shared/base.dart';
|
||||
import '../../shared/common_operations.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Zoom in/out:', () {
|
||||
Future<void> resetAppFlowyScaleFactor(
|
||||
WindowSizeManager windowSizeManager,
|
||||
) async {
|
||||
appflowyScaleFactor = 1.0;
|
||||
await windowSizeManager.setScaleFactor(1.0);
|
||||
}
|
||||
|
||||
testWidgets('Zoom in', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
double currentScaleFactor = 1.0;
|
||||
|
||||
// this value can't be defined in the setUp method, because the windowSizeManager is not initialized yet.
|
||||
final windowSizeManager = WindowSizeManager();
|
||||
await resetAppFlowyScaleFactor(windowSizeManager);
|
||||
|
||||
// zoom in 2 times
|
||||
for (final keycode in zoomInKeyCodes) {
|
||||
if (UniversalPlatform.isLinux &&
|
||||
keycode.logicalKey == LogicalKeyboardKey.add) {
|
||||
// Key LogicalKeyboardKey#79c9b(keyId: "0x0000002b", keyLabel: "+", debugName: "Add") not found in
|
||||
// linux keyCode map
|
||||
continue;
|
||||
}
|
||||
|
||||
// test each keycode 2 times
|
||||
for (var i = 0; i < 2; i++) {
|
||||
await tester.simulateKeyEvent(
|
||||
keycode.logicalKey,
|
||||
isControlPressed: !UniversalPlatform.isMacOS,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
// Register the physical key for the "Add" key, otherwise the test will fail and throw an error:
|
||||
// Physical key for LogicalKeyboardKey#79c9b(keyId: "0x0000002b", keyLabel: "+", debugName: "Add")
|
||||
// not found in known physical keys
|
||||
physicalKey: keycode.logicalKey == LogicalKeyboardKey.add
|
||||
? PhysicalKeyboardKey.equal
|
||||
: null,
|
||||
);
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
currentScaleFactor += 0.1;
|
||||
|
||||
final scaleFactor = await windowSizeManager.getScaleFactor();
|
||||
expect(currentScaleFactor, appflowyScaleFactor);
|
||||
expect(currentScaleFactor, scaleFactor);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('Reset zoom', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
final windowSizeManager = WindowSizeManager();
|
||||
|
||||
for (final keycode in resetZoomKeyCodes) {
|
||||
await tester.simulateKeyEvent(
|
||||
keycode.logicalKey,
|
||||
isControlPressed: !UniversalPlatform.isMacOS,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final scaleFactor = await windowSizeManager.getScaleFactor();
|
||||
expect(1.0, appflowyScaleFactor);
|
||||
expect(1.0, scaleFactor);
|
||||
}
|
||||
});
|
||||
|
||||
testWidgets('Zoom out', (tester) async {
|
||||
await tester.initializeAppFlowy();
|
||||
await tester.tapAnonymousSignInButton();
|
||||
|
||||
double currentScaleFactor = 1.0;
|
||||
|
||||
final windowSizeManager = WindowSizeManager();
|
||||
await resetAppFlowyScaleFactor(windowSizeManager);
|
||||
|
||||
// zoom out 2 times
|
||||
for (final keycode in zoomOutKeyCodes) {
|
||||
if (UniversalPlatform.isLinux &&
|
||||
keycode.logicalKey == LogicalKeyboardKey.numpadSubtract) {
|
||||
// Key LogicalKeyboardKey#2c39f(keyId: "0x20000022d", keyLabel: "Numpad Subtract", debugName: "Numpad
|
||||
// Subtract") not found in linux keyCode map
|
||||
continue;
|
||||
}
|
||||
// test each keycode 2 times
|
||||
for (var i = 0; i < 2; i++) {
|
||||
await tester.simulateKeyEvent(
|
||||
keycode.logicalKey,
|
||||
isControlPressed: !UniversalPlatform.isMacOS,
|
||||
isMetaPressed: UniversalPlatform.isMacOS,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
currentScaleFactor -= 0.1;
|
||||
|
||||
final scaleFactor = await windowSizeManager.getScaleFactor();
|
||||
expect(currentScaleFactor, appflowyScaleFactor);
|
||||
expect(currentScaleFactor, scaleFactor);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import 'desktop/database/database_field_settings_test.dart'
|
||||
as database_field_settings_test;
|
||||
import 'desktop/database/database_field_test.dart' as database_field_test;
|
||||
import 'desktop/database/database_filter_test.dart' as database_filter_test;
|
||||
import 'desktop/database/database_media_test.dart' as database_media_test;
|
||||
import 'desktop/database/database_row_page_test.dart' as database_row_page_test;
|
||||
import 'desktop/database/database_row_test.dart' as database_row_test;
|
||||
import 'desktop/database/database_setting_test.dart' as database_setting_test;
|
||||
import 'desktop/database/database_share_test.dart' as database_share_test;
|
||||
import 'desktop/database/database_sort_test.dart' as database_sort_test;
|
||||
@@ -29,12 +29,12 @@ Future<void> runIntegration2OnDesktop() async {
|
||||
database_field_settings_test.main();
|
||||
database_share_test.main();
|
||||
database_row_page_test.main();
|
||||
database_row_test.main();
|
||||
database_setting_test.main();
|
||||
database_filter_test.main();
|
||||
database_sort_test.main();
|
||||
database_view_test.main();
|
||||
database_calendar_test.main();
|
||||
database_media_test.main();
|
||||
|
||||
// DON'T add more tests here. This is the second test runner for desktop.
|
||||
}
|
||||
@@ -1,6 +1,19 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'desktop/board/board_test_runner.dart' as board_test_runner;
|
||||
import 'desktop/database/database_row_cover_test.dart'
|
||||
as database_row_cover_test;
|
||||
import 'desktop/document/document_title_test.dart' as document_title_test;
|
||||
import 'desktop/document/document_app_lifecycle_test.dart'
|
||||
as document_app_lifecycle_test;
|
||||
import 'desktop/document/document_sub_page_test.dart' as document_sub_page_test;
|
||||
import 'desktop/grid/grid_filter_and_sort_test.dart'
|
||||
as grid_filter_and_sort_test_runner;
|
||||
import 'desktop/grid/grid_reopen_test.dart' as grid_reopen_test_runner;
|
||||
import 'desktop/grid/grid_reorder_row_test.dart'
|
||||
as grid_reorder_row_test_runner;
|
||||
import 'desktop/grid/grid_edit_row_test.dart' as grid_edit_row_test_runner;
|
||||
import 'desktop/grid/grid_row_test.dart' as grid_create_row_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/emoji_shortcut_test.dart' as emoji_shortcut_test;
|
||||
@@ -9,6 +22,7 @@ import 'desktop/uncategorized/hotkeys_test.dart' as hotkeys_test;
|
||||
import 'desktop/uncategorized/import_files_test.dart' as import_files_test;
|
||||
import 'desktop/uncategorized/share_markdown_test.dart' as share_markdown_test;
|
||||
import 'desktop/uncategorized/tabs_test.dart' as tabs_test;
|
||||
import 'desktop/uncategorized/zoom_in_out_test.dart' as zoom_in_out_test;
|
||||
|
||||
Future<void> main() async {
|
||||
await runIntegration3OnDesktop();
|
||||
@@ -30,4 +44,14 @@ Future<void> runIntegration3OnDesktop() async {
|
||||
sidebar_test_runner.main();
|
||||
board_test_runner.main();
|
||||
tabs_test.main();
|
||||
database_row_cover_test.main();
|
||||
grid_reopen_test_runner.main();
|
||||
grid_create_row_test_runner.main();
|
||||
grid_reorder_row_test_runner.main();
|
||||
grid_filter_and_sort_test_runner.main();
|
||||
grid_edit_row_test_runner.main();
|
||||
zoom_in_out_test.main();
|
||||
document_title_test.main();
|
||||
document_sub_page_test.main();
|
||||
document_app_lifecycle_test.main();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import 'workspace/workspace_operations_test.dart' as workspace_operations_test;
|
||||
|
||||
Future<void> main() async {
|
||||
workspace_operations_test.main();
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// 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/constants.dart';
|
||||
import '../../../shared/dir.dart';
|
||||
import '../../../shared/mock/mock_file_picker.dart';
|
||||
import '../../../shared/util.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('workspace operations:', () {
|
||||
testWidgets('create a new workspace', (tester) async {
|
||||
await tester.initializeAppFlowy(
|
||||
cloudType: AuthenticatorType.appflowyCloudSelfHost,
|
||||
);
|
||||
await tester.tapGoogleLoginInButton();
|
||||
await tester.expectToSeeHomePageWithGetStartedPage();
|
||||
|
||||
// click the create a new workspace button
|
||||
await tester.tapButton(find.text(Constants.defaultWorkspaceName));
|
||||
await tester.tapButton(find.text(LocaleKeys.workspace_create.tr()));
|
||||
|
||||
// input the new workspace name
|
||||
final inputField = find.byType(TextFormField);
|
||||
const newWorkspaceName = 'AppFlowy';
|
||||
await tester.enterText(inputField, newWorkspaceName);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// wait for the workspace to be created
|
||||
await tester.pumpUntilFound(
|
||||
find.text(LocaleKeys.workspace_createSuccess.tr()),
|
||||
);
|
||||
|
||||
// expect to see the new workspace
|
||||
expect(find.text(newWorkspaceName), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
File renamed without changes.
@@ -1,5 +1,6 @@
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
import 'mobile/document/page_style_test.dart' as page_style_test;
|
||||
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;
|
||||
|
||||
@@ -7,4 +8,5 @@ Future<void> runIntegrationOnMobile() async {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
anonymous_sign_in_test.main();
|
||||
create_new_page_test.main();
|
||||
page_style_test.main();
|
||||
}
|
||||
@@ -10,9 +10,10 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'util.dart';
|
||||
|
||||
extension AppFlowyAuthTest on WidgetTester {
|
||||
Future<void> tapGoogleLoginInButton() async {
|
||||
Future<void> tapGoogleLoginInButton({bool pumpAndSettle = true}) async {
|
||||
await tapButton(
|
||||
find.byKey(signInWithGoogleButtonKey),
|
||||
pumpAndSettle: pumpAndSettle,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ extension AppFlowyTestBase on WidgetTester {
|
||||
await this.pumpAndSettle(
|
||||
Duration(milliseconds: milliseconds),
|
||||
EnginePhase.sendSemanticsUpdate,
|
||||
const Duration(seconds: 5),
|
||||
const Duration(seconds: 15),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/sidebar_space_menu.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -16,12 +17,16 @@ 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/footer/sidebar_footer.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_new_page_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/shared_widget.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/sidebar_space_header.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';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/menu/view/view_more_action_button.dart';
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/flowy_tab.dart';
|
||||
import 'package:appflowy/workspace/presentation/notifications/widgets/notification_button.dart';
|
||||
@@ -58,6 +63,14 @@ extension CommonOperations on WidgetTester {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> tapContinousAnotherWay() async {
|
||||
// local version
|
||||
await tapButtonWithName(LocaleKeys.signIn_continueAnotherWay.tr());
|
||||
if (Platform.isWindows) {
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
}
|
||||
|
||||
/// Tap the + button on the home page.
|
||||
Future<void> tapAddViewButton({
|
||||
String name = gettingStarted,
|
||||
@@ -227,6 +240,10 @@ extension CommonOperations on WidgetTester {
|
||||
await tapOKButton();
|
||||
}
|
||||
|
||||
Future<void> tapTrashButton() async {
|
||||
await tap(find.byType(SidebarTrashButton));
|
||||
}
|
||||
|
||||
Future<void> tapOKButton() async {
|
||||
final okButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
@@ -236,6 +253,20 @@ extension CommonOperations on WidgetTester {
|
||||
await tapButton(okButton);
|
||||
}
|
||||
|
||||
/// Expand or collapse the page.
|
||||
Future<void> expandOrCollapsePage({
|
||||
required String pageName,
|
||||
required ViewLayoutPB layout,
|
||||
}) async {
|
||||
final page = findPageName(pageName, layout: layout);
|
||||
await hoverOnWidget(page);
|
||||
final expandButton = find.descendant(
|
||||
of: page,
|
||||
matching: find.byType(ViewItemDefaultLeftIcon),
|
||||
);
|
||||
await tapButton(expandButton.first);
|
||||
}
|
||||
|
||||
/// Tap the restore button.
|
||||
///
|
||||
/// the restore button will show after the current page is deleted.
|
||||
@@ -293,10 +324,14 @@ extension CommonOperations on WidgetTester {
|
||||
}
|
||||
await pumpAndSettle();
|
||||
|
||||
final defaultPageName = layout == ViewLayoutPB.Document
|
||||
? '' // the document name is empty by default
|
||||
: LocaleKeys.menuAppHeader_defaultNewPageName.tr();
|
||||
|
||||
// hover on it and change it's name
|
||||
if (name != null) {
|
||||
await hoverOnPageName(
|
||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
defaultPageName,
|
||||
layout: layout,
|
||||
onHover: () async {
|
||||
await renamePage(name);
|
||||
@@ -310,13 +345,87 @@ extension CommonOperations on WidgetTester {
|
||||
if (openAfterCreated) {
|
||||
await openPage(
|
||||
// if the name is null, use the default name
|
||||
name ?? LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
name ?? defaultPageName,
|
||||
layout: layout,
|
||||
);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new page in the space
|
||||
Future<void> createNewPageInSpace({
|
||||
required String spaceName,
|
||||
required ViewLayoutPB layout,
|
||||
bool openAfterCreated = true,
|
||||
String? pageName,
|
||||
}) async {
|
||||
final currentSpace = find.byWidgetPredicate(
|
||||
(widget) => widget is CurrentSpace && widget.space.name == spaceName,
|
||||
);
|
||||
if (currentSpace.evaluate().isEmpty) {
|
||||
throw Exception('Current space not found');
|
||||
}
|
||||
|
||||
await hoverOnWidget(
|
||||
currentSpace,
|
||||
onHover: () async {
|
||||
// click the + button
|
||||
await clickAddPageButtonInSpaceHeader();
|
||||
await tapButtonWithName(layout.menuName);
|
||||
},
|
||||
);
|
||||
await pumpAndSettle();
|
||||
|
||||
if (pageName != null) {
|
||||
// move the cursor to other place to disable to tooltips
|
||||
await tapAt(Offset.zero);
|
||||
|
||||
// hover on new created page and change it's name
|
||||
await hoverOnPageName(
|
||||
'',
|
||||
layout: layout,
|
||||
onHover: () async {
|
||||
await renamePage(pageName);
|
||||
await pumpAndSettle();
|
||||
},
|
||||
);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
// open the page after created
|
||||
if (openAfterCreated) {
|
||||
await openPage(
|
||||
// if the name is null, use the default name
|
||||
pageName ?? LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
layout: layout,
|
||||
);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
}
|
||||
|
||||
/// Click the + button in the space header
|
||||
Future<void> clickAddPageButtonInSpaceHeader() async {
|
||||
final addPageButton = find.descendant(
|
||||
of: find.byType(SidebarSpaceHeader),
|
||||
matching: find.byType(ViewAddButton),
|
||||
);
|
||||
await tapButton(addPageButton);
|
||||
}
|
||||
|
||||
/// Click the + button in the space header
|
||||
Future<void> clickSpaceHeader() async {
|
||||
await tapButton(find.byType(SidebarSpaceHeader));
|
||||
}
|
||||
|
||||
Future<void> openSpace(String spaceName) async {
|
||||
final space = find.descendant(
|
||||
of: find.byType(SidebarSpaceMenuItem),
|
||||
matching: find.text(spaceName),
|
||||
);
|
||||
await tapButton(space);
|
||||
}
|
||||
|
||||
/// Create a new page on the top level
|
||||
Future<void> createNewPage({
|
||||
ViewLayoutPB layout = ViewLayoutPB.Document,
|
||||
bool openAfterCreated = true,
|
||||
@@ -330,6 +439,7 @@ extension CommonOperations on WidgetTester {
|
||||
bool isShiftPressed = false,
|
||||
bool isAltPressed = false,
|
||||
bool isMetaPressed = false,
|
||||
PhysicalKeyboardKey? physicalKey,
|
||||
}) async {
|
||||
if (isControlPressed) {
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.control);
|
||||
@@ -343,8 +453,14 @@ extension CommonOperations on WidgetTester {
|
||||
if (isMetaPressed) {
|
||||
await simulateKeyDownEvent(LogicalKeyboardKey.meta);
|
||||
}
|
||||
await simulateKeyDownEvent(key);
|
||||
await simulateKeyUpEvent(key);
|
||||
await simulateKeyDownEvent(
|
||||
key,
|
||||
physicalKey: physicalKey,
|
||||
);
|
||||
await simulateKeyUpEvent(
|
||||
key,
|
||||
physicalKey: physicalKey,
|
||||
);
|
||||
if (isControlPressed) {
|
||||
await simulateKeyUpEvent(LogicalKeyboardKey.control);
|
||||
}
|
||||
@@ -540,7 +656,11 @@ extension CommonOperations on WidgetTester {
|
||||
expect(createWorkspaceDialog, findsOneWidget);
|
||||
|
||||
// input the workspace name
|
||||
await enterText(find.byType(TextField), name);
|
||||
final workspaceNameInput = find.descendant(
|
||||
of: createWorkspaceDialog,
|
||||
matching: find.byType(TextField),
|
||||
);
|
||||
await enterText(workspaceNameInput, name);
|
||||
|
||||
await tapButtonWithName(LocaleKeys.button_ok.tr(), pumpAndSettle: false);
|
||||
await pump(const Duration(seconds: 5));
|
||||
@@ -572,8 +692,7 @@ extension CommonOperations on WidgetTester {
|
||||
|
||||
Future<void> openMoreViewActions() async {
|
||||
final button = find.byType(MoreViewActions);
|
||||
await tap(button);
|
||||
await pumpAndSettle();
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
/// Presses on the Duplicate ViewAction in the [MoreViewActions] popup.
|
||||
@@ -581,12 +700,9 @@ extension CommonOperations on WidgetTester {
|
||||
/// [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,
|
||||
),
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewAction && widget.type == ViewMoreActionType.duplicate,
|
||||
);
|
||||
await tap(button);
|
||||
await pump();
|
||||
@@ -601,7 +717,7 @@ extension CommonOperations on WidgetTester {
|
||||
of: find.byType(ListView),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is ViewAction && widget.type == ViewActionType.delete,
|
||||
widget is ViewAction && widget.type == ViewMoreActionType.delete,
|
||||
),
|
||||
);
|
||||
await tap(button);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
class Constants {
|
||||
// this page name is default page name in the new workspace
|
||||
static const gettingStartedPageName = 'Getting started';
|
||||
static const toDosPageName = 'To-dos';
|
||||
static const generalSpaceName = 'General';
|
||||
|
||||
static const defaultWorkspaceName = 'My Workspace';
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
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';
|
||||
@@ -60,7 +59,7 @@ class TestWorkspaceService {
|
||||
final inputStream =
|
||||
InputFileStream(await workspace.zip.then((value) => value.path));
|
||||
final archive = ZipDecoder().decodeBuffer(inputStream);
|
||||
extractArchiveToDisk(
|
||||
await extractArchiveToDisk(
|
||||
archive,
|
||||
await TestWorkspace._parent.then((value) => value.path),
|
||||
);
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/plugins/database/application/field/filter_entities.dart';
|
||||
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||
import 'package:appflowy/shared/icon_emoji_picker/icon_picker.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -20,13 +23,14 @@ import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/calculations/calculation_type_item.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/common/type_option_separator.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checkbox.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checklist/checklist.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/checklist.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/choicechip.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/select_option/option_list.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/select_option/select_option.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/text.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/date.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/create_filter_list.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/disclosure_button.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_menu_item.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/footer/grid_footer.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/desktop_field_cell.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/row/row.dart';
|
||||
@@ -95,8 +99,11 @@ import 'common_operations.dart';
|
||||
import 'expectation.dart';
|
||||
import 'mock/mock_file_picker.dart';
|
||||
|
||||
const v020GridFileName = "v020.afdb";
|
||||
const v069GridFileName = "v069.afdb";
|
||||
|
||||
extension AppFlowyDatabaseTest on WidgetTester {
|
||||
Future<void> openV020database() async {
|
||||
Future<void> openTestDatabase(String fileName) async {
|
||||
final context = await initializeAppFlowy();
|
||||
await tapAnonymousSignInButton();
|
||||
|
||||
@@ -106,29 +113,24 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapAddViewButton();
|
||||
await tapImportButton();
|
||||
|
||||
final testFileNames = ['v020.afdb'];
|
||||
final paths = <String>[];
|
||||
for (final fileName in testFileNames) {
|
||||
// Don't use the p.join to build the path that used in loadString. It
|
||||
// is not working on windows.
|
||||
final str = await rootBundle
|
||||
.loadString("assets/test/workspaces/database/$fileName");
|
||||
// Don't use the p.join to build the path that used in loadString. It
|
||||
// is not working on windows.
|
||||
final str = await rootBundle
|
||||
.loadString("assets/test/workspaces/database/$fileName");
|
||||
|
||||
// Write the content to the file.
|
||||
final path = p.join(
|
||||
context.applicationDataDirectory,
|
||||
fileName,
|
||||
);
|
||||
paths.add(path);
|
||||
File(path).writeAsStringSync(str);
|
||||
}
|
||||
// Write the content to the file.
|
||||
final path = p.join(
|
||||
context.applicationDataDirectory,
|
||||
fileName,
|
||||
);
|
||||
final pageName = p.basenameWithoutExtension(path);
|
||||
File(path).writeAsStringSync(str);
|
||||
// mock get files
|
||||
mockPickFilePaths(
|
||||
paths: paths,
|
||||
paths: [path],
|
||||
);
|
||||
await tapDatabaseRawDataButton();
|
||||
await pumpAndSettle();
|
||||
await openPage('v020', layout: ViewLayoutPB.Grid);
|
||||
await openPage(pageName, layout: ViewLayoutPB.Grid);
|
||||
}
|
||||
|
||||
Future<void> hoverOnFirstRowOfGrid([Future<void> Function()? onHover]) async {
|
||||
@@ -149,6 +151,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
|
||||
expect(cell, findsOneWidget);
|
||||
await enterText(cell, input);
|
||||
await testTextInput.receiveAction(TextInputAction.done);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
@@ -244,10 +247,10 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> assertMultiSelectOption({
|
||||
void assertMultiSelectOption({
|
||||
required int rowIndex,
|
||||
required List<String> contents,
|
||||
}) async {
|
||||
}) {
|
||||
final findCell = cellFinder(rowIndex, FieldType.MultiSelect);
|
||||
for (final content in contents) {
|
||||
if (content.isNotEmpty) {
|
||||
@@ -415,10 +418,10 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(option);
|
||||
}
|
||||
|
||||
Future<void> findSelectOptionWithNameInGrid({
|
||||
void findSelectOptionWithNameInGrid({
|
||||
required int rowIndex,
|
||||
required String name,
|
||||
}) async {
|
||||
}) {
|
||||
final findRow = find.byType(GridRow);
|
||||
final option = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
@@ -430,10 +433,10 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
expect(cell, findsOneWidget);
|
||||
}
|
||||
|
||||
Future<void> assertNumberOfSelectedOptionsInGrid({
|
||||
void assertNumberOfSelectedOptionsInGrid({
|
||||
required int rowIndex,
|
||||
required Matcher matcher,
|
||||
}) async {
|
||||
}) {
|
||||
final findRow = find.byType(GridRow);
|
||||
|
||||
final options = find.byWidgetPredicate(
|
||||
@@ -571,8 +574,19 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
/// Used to open the add cover popover, by pressing on "Add cover"-button.
|
||||
///
|
||||
/// Should call [hoverRowBanner] first.
|
||||
///
|
||||
Future<void> tapAddCoverButton() async {
|
||||
await tapButtonWithName(
|
||||
LocaleKeys.document_plugins_cover_addCover.tr(),
|
||||
);
|
||||
await pumpAndSettle();
|
||||
}
|
||||
|
||||
Future<void> openEmojiPicker() async =>
|
||||
tapButton(find.byType(AddEmojiButton));
|
||||
tapButton(find.text(LocaleKeys.document_plugins_cover_addIcon.tr()));
|
||||
|
||||
Future<void> tapDateCellInRowDetailPage() async {
|
||||
final findDateCell = find.byType(EditableDateCell);
|
||||
@@ -670,6 +684,53 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await dismissFieldEditor();
|
||||
}
|
||||
|
||||
Future<void> changeFieldIcon(String icon) async {
|
||||
await tapButton(find.byType(FieldEditIconButton));
|
||||
if (icon.isEmpty) {
|
||||
final button = find.descendant(
|
||||
of: find.byType(FlowyIconEmojiPicker),
|
||||
matching: find.text(
|
||||
LocaleKeys.button_remove.tr(),
|
||||
),
|
||||
);
|
||||
await tapButton(button);
|
||||
} else {
|
||||
final svgContent = kIconGroups?.findSvgContent(icon);
|
||||
await tapButton(
|
||||
find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svgString == svgContent,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void assertFieldSvg(String name, FieldType fieldType) {
|
||||
final svgFinder = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svg == fieldType.svgData,
|
||||
);
|
||||
final fieldButton = find.byWidgetPredicate(
|
||||
(widget) => widget is FieldCellButton && widget.field.name == name,
|
||||
);
|
||||
expect(
|
||||
find.descendant(of: fieldButton, matching: svgFinder),
|
||||
findsOneWidget,
|
||||
);
|
||||
}
|
||||
|
||||
void assertFieldCustomSvg(String name, String svg) {
|
||||
final svgContent = kIconGroups?.findSvgContent(svg);
|
||||
final svgFinder = find.byWidgetPredicate(
|
||||
(widget) => widget is FlowySvg && widget.svgString == svgContent,
|
||||
);
|
||||
final fieldButton = find.byWidgetPredicate(
|
||||
(widget) => widget is FieldCellButton && widget.field.name == name,
|
||||
);
|
||||
expect(
|
||||
find.descendant(of: fieldButton, matching: svgFinder),
|
||||
findsOneWidget,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> changeCalculateAtIndex(int index, CalculationType type) async {
|
||||
await tap(find.byType(CalculateCell).at(index));
|
||||
await pumpAndSettle();
|
||||
@@ -787,7 +848,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
expect(finder, findsWidgets);
|
||||
}
|
||||
|
||||
Future<void> assertNumberOfRowsInGridPage(int num) async {
|
||||
void assertNumberOfRowsInGridPage(int num) {
|
||||
expect(
|
||||
find.byType(GridRow, skipOffstage: false),
|
||||
findsNWidgets(num),
|
||||
@@ -872,21 +933,51 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(find.byType(GridAddRowButton));
|
||||
}
|
||||
|
||||
Future<void> tapCreateRowButtonInRowMenuOfGrid() async {
|
||||
Future<void> tapCreateRowButtonAfterHoveringOnGridRow() async {
|
||||
await tapButton(find.byType(InsertRowButton));
|
||||
}
|
||||
|
||||
Future<void> tapRowMenuButtonInGrid() async {
|
||||
expect(find.byType(RowMenuButton), findsOneWidget);
|
||||
await tapButton(find.byType(RowMenuButton));
|
||||
}
|
||||
|
||||
/// Should call [tapRowMenuButtonInGrid] first.
|
||||
Future<void> tapCreateRowAboveButtonInRowMenu() async {
|
||||
await tapButtonWithName(LocaleKeys.grid_row_insertRecordAbove.tr());
|
||||
}
|
||||
|
||||
/// 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> reorderRow(
|
||||
String from,
|
||||
String to,
|
||||
) async {
|
||||
final fromRow = find.byWidgetPredicate(
|
||||
(widget) => widget is GridRow && widget.rowId == from,
|
||||
);
|
||||
final toRow = find.byWidgetPredicate(
|
||||
(widget) => widget is GridRow && widget.rowId == to,
|
||||
);
|
||||
await hoverOnWidget(
|
||||
fromRow,
|
||||
onHover: () async {
|
||||
final dragElement = find.descendant(
|
||||
of: fromRow,
|
||||
matching: find.byType(ReorderableDragStartListener),
|
||||
);
|
||||
await timedDrag(
|
||||
dragElement,
|
||||
getCenter(toRow) - getCenter(fromRow),
|
||||
const Duration(milliseconds: 200),
|
||||
);
|
||||
await pumpAndSettle();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> createField(
|
||||
FieldType fieldType, {
|
||||
String? name,
|
||||
@@ -918,7 +1009,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
Future<void> tapCreateFilterByFieldType(FieldType type, String title) async {
|
||||
final findFilter = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is GridFilterPropertyCell &&
|
||||
widget is FilterableFieldButton &&
|
||||
widget.fieldInfo.fieldType == type &&
|
||||
widget.fieldInfo.name == title,
|
||||
);
|
||||
@@ -926,8 +1017,9 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
Future<void> tapFilterButtonInGrid(String name) async {
|
||||
final findFilter = find.byType(FilterMenuItem);
|
||||
final button = find.descendant(of: findFilter, matching: find.text(name));
|
||||
final button = find.byWidgetPredicate(
|
||||
(widget) => widget is ChoiceChipButton && widget.fieldInfo.name == name,
|
||||
);
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
@@ -965,12 +1057,15 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
/// Must call [tapSortMenuInSettingBar] first.
|
||||
Future<void> tapSortButtonByName(String name) async {
|
||||
final findSortItem = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is DatabaseSortItem && widget.sortInfo.fieldInfo.name == name,
|
||||
Future<void> tapEditSortConditionButtonByFieldName(String name) async {
|
||||
final sortItem = find.descendant(
|
||||
of: find.ancestor(
|
||||
of: find.text(name),
|
||||
matching: find.byType(DatabaseSortItem),
|
||||
),
|
||||
matching: find.byType(SortConditionButton),
|
||||
);
|
||||
await tapButton(findSortItem);
|
||||
await tapButton(sortItem);
|
||||
}
|
||||
|
||||
/// Must call [tapSortMenuInSettingBar] first.
|
||||
@@ -978,18 +1073,26 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
(FieldType, String) from,
|
||||
(FieldType, String) to,
|
||||
) async {
|
||||
final fromSortItem = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is DatabaseSortItem &&
|
||||
widget.sortInfo.fieldInfo.fieldType == from.$1 &&
|
||||
widget.sortInfo.fieldInfo.name == from.$2,
|
||||
final fromSortItem = find.ancestor(
|
||||
of: find.text(from.$2),
|
||||
matching: find.byType(DatabaseSortItem),
|
||||
);
|
||||
final toSortItem = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is DatabaseSortItem &&
|
||||
widget.sortInfo.fieldInfo.fieldType == to.$1 &&
|
||||
widget.sortInfo.fieldInfo.name == to.$2,
|
||||
final toSortItem = find.ancestor(
|
||||
of: find.text(to.$2),
|
||||
matching: find.byType(DatabaseSortItem),
|
||||
);
|
||||
// final fromSortItem = find.byWidgetPredicate(
|
||||
// (widget) =>
|
||||
// widget is DatabaseSortItem &&
|
||||
// widget.sort.fieldInfo.fieldType == from.$1 &&
|
||||
// widget.sort.fieldInfo.name == from.$2,
|
||||
// );
|
||||
// final toSortItem = find.byWidgetPredicate(
|
||||
// (widget) =>
|
||||
// widget is DatabaseSortItem &&
|
||||
// widget.sort.fieldInfo.fieldType == to.$1 &&
|
||||
// widget.sort.fieldInfo.name == to.$2,
|
||||
// );
|
||||
final dragElement = find.descendant(
|
||||
of: fromSortItem,
|
||||
matching: find.byType(ReorderableDragStartListener),
|
||||
@@ -998,16 +1101,13 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await pumpAndSettle(const Duration(milliseconds: 200));
|
||||
}
|
||||
|
||||
/// Must call [tapSortButtonByName] first.
|
||||
/// Must call [tapEditSortConditionButtonByFieldName] first.
|
||||
Future<void> tapSortByDescending() async {
|
||||
await tapButton(
|
||||
find.descendant(
|
||||
of: find.byType(OrderPannelItem),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is FlowyText &&
|
||||
widget.text == LocaleKeys.grid_sort_descending.tr(),
|
||||
),
|
||||
find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is OrderPanelItem &&
|
||||
widget.condition == SortConditionPB.Descending,
|
||||
),
|
||||
);
|
||||
await sendKeyEvent(LogicalKeyboardKey.escape);
|
||||
@@ -1015,7 +1115,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
}
|
||||
|
||||
/// Must call [tapSortMenuInSettingBar] first.
|
||||
Future<void> tapAllSortButton() async {
|
||||
Future<void> tapDeleteAllSortsButton() async {
|
||||
await tapButton(find.byType(DeleteAllSortsButton));
|
||||
}
|
||||
|
||||
@@ -1057,6 +1157,10 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(find.byType(ChecklistFilterConditionList));
|
||||
}
|
||||
|
||||
Future<void> tapDateFilterButtonInGrid() async {
|
||||
await tapButton(find.byType(DateFilterConditionList));
|
||||
}
|
||||
|
||||
/// The [SelectOptionFilterList] must show up first.
|
||||
Future<void> tapOptionFilterWithName(String name) async {
|
||||
final findCell = find.descendant(
|
||||
@@ -1090,6 +1194,15 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
Future<void> tapDateFilterCondition(DateTimeFilterCondition condition) async {
|
||||
final button = find.descendant(
|
||||
of: find.byType(HoverButton),
|
||||
matching: find.text(condition.filterName),
|
||||
);
|
||||
|
||||
await tapButton(button);
|
||||
}
|
||||
|
||||
/// Should call [tapDatabaseSettingButton] first.
|
||||
Future<void> tapViewPropertiesButton() async {
|
||||
final findSettingItem = find.byType(DatabaseSettingsList);
|
||||
@@ -1252,7 +1365,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
matching: find.byType(EventCard),
|
||||
);
|
||||
|
||||
await tapButton(cards.at(index));
|
||||
await tapButton(cards.at(index), milliseconds: 1000);
|
||||
}
|
||||
|
||||
void assertEventEditorOpen() =>
|
||||
@@ -1268,6 +1381,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
);
|
||||
|
||||
await enterText(textField, title);
|
||||
await testTextInput.receiveAction(TextInputAction.done);
|
||||
await pumpAndSettle(const Duration(milliseconds: 300));
|
||||
}
|
||||
|
||||
|
||||
+91
-6
@@ -1,21 +1,24 @@
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/base/emoji/emoji_picker.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_add_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/block_action_option_button.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/drag_to_reorder/draggable_option_button.dart';
|
||||
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/cover_title.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_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/shared/icon_emoji_picker/emoji_skin_tone.dart';
|
||||
import 'package:appflowy/shared/icon_emoji_picker/flowy_icon_emoji_picker.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_emoji_mart/flutter_emoji_mart.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@@ -33,11 +36,19 @@ class EditorOperations {
|
||||
EditorState getCurrentEditorState() =>
|
||||
tester.widget<AppFlowyEditor>(find.byType(AppFlowyEditor)).editorState;
|
||||
|
||||
Node getNodeAtPath(Path path) {
|
||||
final editorState = getCurrentEditorState();
|
||||
return editorState.getNodeAtPath(path)!;
|
||||
}
|
||||
|
||||
/// Tap the line of editor at [index]
|
||||
Future<void> tapLineOfEditorAt(int index) async {
|
||||
final textBlocks = find.byType(AppFlowyRichText);
|
||||
index = index.clamp(0, textBlocks.evaluate().length - 1);
|
||||
await tester.tapAt(tester.getTopRight(textBlocks.at(index)));
|
||||
final center = tester.getCenter(textBlocks.at(index));
|
||||
final right = tester.getTopRight(textBlocks.at(index));
|
||||
final centerRight = Offset(right.dx, center.dy);
|
||||
await tester.tapAt(centerRight);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
@@ -205,7 +216,7 @@ class EditorOperations {
|
||||
}
|
||||
|
||||
/// Update the editor's selection
|
||||
Future<void> updateSelection(Selection selection) async {
|
||||
Future<void> updateSelection(Selection? selection) async {
|
||||
final editorState = getCurrentEditorState();
|
||||
unawaited(
|
||||
editorState.updateSelectionWithReason(
|
||||
@@ -266,4 +277,78 @@ class EditorOperations {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Drag block
|
||||
///
|
||||
/// [offset] is the offset to move the block.
|
||||
///
|
||||
/// [path] is the path of the block to move.
|
||||
Future<void> dragBlock(
|
||||
Path path,
|
||||
Offset offset,
|
||||
) async {
|
||||
final dragToMoveAction = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is DraggableOptionButton &&
|
||||
widget.blockComponentContext.node.path.equals(path),
|
||||
);
|
||||
|
||||
await tester.hoverOnWidget(
|
||||
dragToMoveAction,
|
||||
onHover: () async {
|
||||
final dragToMoveTooltip = find.findFlowyTooltip(
|
||||
LocaleKeys.blockActions_dragTooltip.tr(),
|
||||
);
|
||||
await tester.pumpUntilFound(dragToMoveTooltip);
|
||||
final location = tester.getCenter(dragToMoveAction);
|
||||
final gesture = await tester.startGesture(
|
||||
location,
|
||||
pointer: 7,
|
||||
);
|
||||
await tester.pump();
|
||||
|
||||
// divide the steps to small move to avoid the drag area not found error
|
||||
const steps = 5;
|
||||
final stepOffset = Offset(offset.dx / steps, offset.dy / steps);
|
||||
|
||||
for (var i = 0; i < steps; i++) {
|
||||
await gesture.moveBy(stepOffset);
|
||||
await tester.pump(Durations.short1);
|
||||
}
|
||||
|
||||
// check if the drag to move action is dragging
|
||||
expect(
|
||||
isDraggingAppFlowyEditorBlock.value,
|
||||
isTrue,
|
||||
);
|
||||
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
},
|
||||
);
|
||||
await tester.pumpAndSettle(Durations.short1);
|
||||
}
|
||||
|
||||
Finder findDocumentTitle(String title) {
|
||||
return find.descendant(
|
||||
of: find.byType(CoverTitle),
|
||||
matching: find.byWidgetPredicate(
|
||||
(widget) {
|
||||
if (widget is! TextField) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (widget.controller?.text == title) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (title.isEmpty) {
|
||||
return widget.controller?.text.isEmpty ?? false;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
||||
import 'package:appflowy/mobile/presentation/presentation.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';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/document_cover_widget.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/header/emoji_icon_widget.dart';
|
||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
|
||||
@@ -11,11 +12,11 @@ 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';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:universal_platform/universal_platform.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
@@ -25,9 +26,15 @@ const String gettingStarted = 'Getting started';
|
||||
extension Expectation on WidgetTester {
|
||||
/// Expect to see the home page and with a default read me page.
|
||||
Future<void> expectToSeeHomePageWithGetStartedPage() async {
|
||||
final finder = find.byType(HomeStack);
|
||||
await pumpUntilFound(finder);
|
||||
expect(finder, findsOneWidget);
|
||||
if (UniversalPlatform.isDesktopOrWeb) {
|
||||
final finder = find.byType(HomeStack);
|
||||
await pumpUntilFound(finder);
|
||||
expect(finder, findsOneWidget);
|
||||
} else if (UniversalPlatform.isMobile) {
|
||||
final finder = find.byType(MobileHomePage);
|
||||
await pumpUntilFound(finder);
|
||||
expect(finder, findsOneWidget);
|
||||
}
|
||||
|
||||
final docFinder = find.textContaining(gettingStarted);
|
||||
await pumpUntilFound(docFinder);
|
||||
@@ -184,7 +191,7 @@ extension Expectation on WidgetTester {
|
||||
String? parentName,
|
||||
ViewLayoutPB parentLayout = ViewLayoutPB.Document,
|
||||
}) {
|
||||
if (PlatformExtension.isDesktop) {
|
||||
if (UniversalPlatform.isDesktop) {
|
||||
if (parentName == null) {
|
||||
return find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export 'auth_operation.dart';
|
||||
export 'base.dart';
|
||||
export 'common_operations.dart';
|
||||
export 'settings.dart';
|
||||
export 'data.dart';
|
||||
export 'document_test_operations.dart';
|
||||
export 'expectation.dart';
|
||||
export 'editor_test_operations.dart';
|
||||
export 'mock/mock_url_launcher.dart';
|
||||
export 'ime.dart';
|
||||
export 'auth_operation.dart';
|
||||
export 'mock/mock_url_launcher.dart';
|
||||
export 'settings.dart';
|
||||
@@ -170,7 +170,7 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: 5ef33d0d295a89d9d16bb81b0e3b0d5f70d6c875
|
||||
app_links: e70ca16b4b0f88253b3b3660200d4a10b4ea9795
|
||||
appflowy_backend: 144c20d8bfb298c4e10fa3fa6701a9f41bf98b88
|
||||
connectivity_plus: bf0076dd84a130856aa636df1c71ccaff908fa1d
|
||||
device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d
|
||||
@@ -180,25 +180,25 @@ SPEC CHECKSUMS:
|
||||
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
|
||||
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
||||
irondash_engine_context: 3458bf979b90d616ffb8ae03a150bafe2e860cc9
|
||||
keyboard_height_plugin: 43fa8bba20fd5c4fdeed5076466b8b9d43cc6b86
|
||||
open_filex: 6e26e659846ec990262224a12ef1c528bb4edbe4
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
||||
Sentry: 1fe34e9c2cbba1e347623610d26db121dcb569f1
|
||||
sentry_flutter: a39c2a2d67d5e5b9cb0b94a4985c76dd5b3fc737
|
||||
share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5
|
||||
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
|
||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||
super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7
|
||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
|
||||
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ class KVKeys {
|
||||
'kDocumentAppearanceCursorColor';
|
||||
static const String kDocumentAppearanceSelectionColor =
|
||||
'kDocumentAppearanceSelectionColor';
|
||||
static const String kDocumentAppearanceWidth = 'kDocumentAppearanceWidth';
|
||||
|
||||
/// The key for saving the expanded views
|
||||
///
|
||||
@@ -56,8 +57,6 @@ class KVKeys {
|
||||
|
||||
static const String kCloudType = 'kCloudType';
|
||||
static const String kAppflowyCloudBaseURL = 'kAppFlowyCloudBaseURL';
|
||||
static const String kSupabaseURL = 'kSupabaseURL';
|
||||
static const String kSupabaseAnonKey = 'kSupabaseAnonKey';
|
||||
|
||||
/// The key for saving the text scale factor.
|
||||
///
|
||||
|
||||
+6
-24
@@ -14,8 +14,12 @@ import 'package:appflowy_backend/log.dart';
|
||||
/// [ty] - The type of cloud to be set. It must be one of the values from
|
||||
/// [AuthenticatorType] enum. The corresponding integer value of the enum is stored:
|
||||
/// - `CloudType.local` is stored as "0".
|
||||
/// - `CloudType.supabase` is stored as "1".
|
||||
/// - `CloudType.appflowyCloud` is stored as "2".
|
||||
///
|
||||
/// The gap between [AuthenticatorType.local] and [AuthenticatorType.appflowyCloud] is
|
||||
/// due to previously supporting Supabase, this has been deprecated since and removed.
|
||||
/// To not cause conflicts with older clients, we keep the gap.
|
||||
///
|
||||
Future<void> _setAuthenticatorType(AuthenticatorType ty) async {
|
||||
switch (ty) {
|
||||
case AuthenticatorType.local:
|
||||
@@ -84,7 +88,7 @@ Future<AuthenticatorType> getAuthenticatorType() async {
|
||||
/// A boolean value indicating whether authentication is enabled. It returns
|
||||
/// `true` if the application is in release or develop mode, and the cloud type
|
||||
/// is not set to `CloudType.local`. Additionally, it checks if either the
|
||||
/// AppFlowy Cloud or Supabase configuration is valid.
|
||||
/// AppFlowy Cloud configuration is valid.
|
||||
/// Returns `false` otherwise.
|
||||
bool get isAuthEnabled {
|
||||
final env = getIt<AppFlowyCloudSharedEnv>();
|
||||
@@ -286,25 +290,3 @@ Future<String> _getAppFlowyCloudWSUrl(String baseURL) async {
|
||||
Future<String> _getAppFlowyCloudGotrueUrl(String baseURL) async {
|
||||
return "$baseURL/gotrue";
|
||||
}
|
||||
|
||||
Future<void> setSupabaseServer(
|
||||
String? url,
|
||||
String? anonKey,
|
||||
) async {
|
||||
assert(
|
||||
(url != null && anonKey != null) || (url == null && anonKey == null),
|
||||
"Either both Supabase URL and anon key must be set, or both should be unset",
|
||||
);
|
||||
|
||||
if (url == null) {
|
||||
await getIt<KeyValueStorage>().remove(KVKeys.kSupabaseURL);
|
||||
} else {
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kSupabaseURL, url);
|
||||
}
|
||||
|
||||
if (anonKey == null) {
|
||||
await getIt<KeyValueStorage>().remove(KVKeys.kSupabaseAnonKey);
|
||||
} else {
|
||||
await getIt<KeyValueStorage>().set(KVKeys.kSupabaseAnonKey, anonKey);
|
||||
}
|
||||
}
|
||||
@@ -391,17 +391,23 @@ class _AFDropdownMenuState<T> extends State<AFDropdownMenu<T>> {
|
||||
);
|
||||
}
|
||||
|
||||
// Remove the code here, it will throw a FlutterError
|
||||
// Unless we upgrade to Flutter 3.24 https://github.com/flutter/flutter/issues/146764
|
||||
void scrollToHighlight() {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) {
|
||||
final BuildContext? highlightContext =
|
||||
buttonItemKeys[currentHighlight!].currentContext;
|
||||
if (highlightContext != null) {
|
||||
Scrollable.ensureVisible(highlightContext);
|
||||
}
|
||||
},
|
||||
debugLabel: 'DropdownMenu.scrollToHighlight',
|
||||
);
|
||||
// WidgetsBinding.instance.addPostFrameCallback(
|
||||
// (_) {
|
||||
// // try {
|
||||
// final BuildContext? highlightContext =
|
||||
// buttonItemKeys[currentHighlight!].currentContext;
|
||||
// if (highlightContext != null) {
|
||||
// Scrollable.ensureVisible(highlightContext);
|
||||
// }
|
||||
// } catch (_) {
|
||||
// return;
|
||||
// }
|
||||
// },
|
||||
// debugLabel: 'DropdownMenu.scrollToHighlight',
|
||||
// );
|
||||
}
|
||||
|
||||
double? getWidth(GlobalKey key) {
|
||||
|
||||
+9
-2
@@ -49,7 +49,13 @@ class BlockActionBottomSheet extends StatelessWidget {
|
||||
FlowyOptionTile.text(
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
leftIcon: const FlowySvg(FlowySvgs.m_duplicate_s),
|
||||
leftIcon: const Padding(
|
||||
padding: EdgeInsets.all(2),
|
||||
child: FlowySvg(
|
||||
FlowySvgs.copy_s,
|
||||
size: Size.square(16),
|
||||
),
|
||||
),
|
||||
onTap: () => onAction(BlockActionBottomSheetType.duplicate),
|
||||
),
|
||||
|
||||
@@ -59,7 +65,8 @@ class BlockActionBottomSheet extends StatelessWidget {
|
||||
showTopBorder: false,
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
leftIcon: FlowySvg(
|
||||
FlowySvgs.m_delete_s,
|
||||
FlowySvgs.trash_s,
|
||||
size: const Size.square(18),
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
textColor: Theme.of(context).colorScheme.error,
|
||||
|
||||
+15
@@ -48,12 +48,14 @@ Future<T?> showMobileBottomSheet<T>(
|
||||
double? elevation,
|
||||
bool showDoneButton = false,
|
||||
bool enableDraggableScrollable = false,
|
||||
bool enableScrollable = false,
|
||||
// this field is only used if showDragHandle is true
|
||||
Widget Function(BuildContext, ScrollController)? scrollableWidgetBuilder,
|
||||
// only used when enableDraggableScrollable is true
|
||||
double minChildSize = 0.5,
|
||||
double maxChildSize = 0.8,
|
||||
double initialChildSize = 0.51,
|
||||
double bottomSheetPadding = 0,
|
||||
}) async {
|
||||
assert(
|
||||
showHeader ||
|
||||
@@ -151,6 +153,19 @@ Future<T?> showMobileBottomSheet<T>(
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if (enableScrollable) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...children,
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
VSpace(bottomSheetPadding),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// ----- content area -----
|
||||
|
||||
+30
-25
@@ -1,3 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/database/board/board.dart';
|
||||
@@ -5,11 +7,11 @@ import 'package:appflowy/mobile/presentation/database/board/widgets/group_card_h
|
||||
import 'package:appflowy/mobile/presentation/database/card/card.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/board/application/board_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_extension.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/card/card.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/mobile_board_card_cell_style.dart';
|
||||
import 'package:appflowy/shared/flowy_error_page.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
||||
@@ -17,7 +19,6 @@ import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
@@ -235,29 +236,33 @@ class _BoardContentState extends State<_BoardContent> {
|
||||
key: ValueKey(groupItemId),
|
||||
margin: cardMargin,
|
||||
decoration: _makeBoxDecoration(context),
|
||||
child: RowCard(
|
||||
fieldController: boardBloc.fieldController,
|
||||
rowMeta: rowMeta,
|
||||
viewId: boardBloc.viewId,
|
||||
rowCache: boardBloc.rowCache,
|
||||
groupingFieldId: groupItem.fieldInfo.id,
|
||||
isEditing: false,
|
||||
cellBuilder: cellBuilder,
|
||||
onTap: (context) {
|
||||
context.push(
|
||||
MobileRowDetailPage.routeName,
|
||||
extra: {
|
||||
MobileRowDetailPage.argRowId: rowMeta.id,
|
||||
MobileRowDetailPage.argDatabaseController:
|
||||
context.read<BoardBloc>().databaseController,
|
||||
},
|
||||
);
|
||||
},
|
||||
onStartEditing: () {},
|
||||
onEndEditing: () {},
|
||||
styleConfiguration: RowCardStyleConfiguration(
|
||||
cellStyleMap: mobileBoardCardCellStyleMap(context),
|
||||
showAccessory: false,
|
||||
child: BlocProvider.value(
|
||||
value: boardBloc,
|
||||
child: RowCard(
|
||||
fieldController: boardBloc.fieldController,
|
||||
rowMeta: rowMeta,
|
||||
viewId: boardBloc.viewId,
|
||||
rowCache: boardBloc.rowCache,
|
||||
groupingFieldId: groupItem.fieldInfo.id,
|
||||
isEditing: false,
|
||||
cellBuilder: cellBuilder,
|
||||
onTap: (context) {
|
||||
context.push(
|
||||
MobileRowDetailPage.routeName,
|
||||
extra: {
|
||||
MobileRowDetailPage.argRowId: rowMeta.id,
|
||||
MobileRowDetailPage.argDatabaseController:
|
||||
context.read<BoardBloc>().databaseController,
|
||||
},
|
||||
);
|
||||
},
|
||||
onStartEditing: () {},
|
||||
onEndEditing: () {},
|
||||
styleConfiguration: RowCardStyleConfiguration(
|
||||
cellStyleMap: mobileBoardCardCellStyleMap(context),
|
||||
showAccessory: false,
|
||||
),
|
||||
userProfile: boardBloc.userProfile,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
|
||||
import 'package:appflowy/plugins/database/board/application/board_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/field_type_extension.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
+207
-79
@@ -21,6 +21,10 @@ import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dar
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_skeleton/text.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/row_property.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/file/file_upload_menu.dart';
|
||||
import 'package:appflowy/plugins/document/presentation/editor_plugins/file/file_util.dart';
|
||||
import 'package:appflowy/shared/af_image.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/file_entities.pbenum.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/row_entities.pb.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
@@ -146,6 +150,58 @@ class _MobileRowDetailPageState extends State<MobileRowDetailPage> {
|
||||
text: LocaleKeys.button_duplicate.tr(),
|
||||
),
|
||||
const Divider(height: 8.5, thickness: 0.5),
|
||||
MobileQuickActionButton(
|
||||
onTap: () => showMobileBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.grid_media_addFileMobile.tr(),
|
||||
showHeader: true,
|
||||
showCloseButton: true,
|
||||
showDragHandle: true,
|
||||
builder: (dialogContext) => Container(
|
||||
margin: const EdgeInsets.only(top: 12),
|
||||
constraints: const BoxConstraints(
|
||||
maxHeight: 340,
|
||||
minHeight: 80,
|
||||
),
|
||||
child: FileUploadMenu(
|
||||
onInsertLocalFile: (files) async {
|
||||
context
|
||||
..pop()
|
||||
..pop();
|
||||
|
||||
if (_bloc.state.currentRowId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
await insertLocalFiles(
|
||||
context,
|
||||
files,
|
||||
userProfile: _bloc.userProfile,
|
||||
documentId: _bloc.state.currentRowId!,
|
||||
onUploadSuccess: (file, path, isLocalMode) {
|
||||
_bloc.add(
|
||||
MobileRowDetailEvent.addCover(
|
||||
RowCoverPB(
|
||||
data: path,
|
||||
uploadType: isLocalMode
|
||||
? FileUploadTypePB.LocalFile
|
||||
: FileUploadTypePB.CloudFile,
|
||||
coverType: CoverTypePB.FileCover,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
onInsertNetworkFile: (url) async =>
|
||||
_onInsertNetworkFile(url, context),
|
||||
),
|
||||
),
|
||||
),
|
||||
icon: FlowySvgs.add_cover_s,
|
||||
text: 'Add cover',
|
||||
),
|
||||
const Divider(height: 8.5, thickness: 0.5),
|
||||
MobileQuickActionButton(
|
||||
onTap: () => _performAction(viewId, _bloc.state.currentRowId, true),
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
@@ -178,6 +234,38 @@ class _MobileRowDetailPageState extends State<MobileRowDetailPage> {
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onInsertNetworkFile(
|
||||
String url,
|
||||
BuildContext context,
|
||||
) async {
|
||||
context
|
||||
..pop()
|
||||
..pop();
|
||||
|
||||
if (url.isEmpty) return;
|
||||
final uri = Uri.tryParse(url);
|
||||
if (uri == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String name = uri.pathSegments.isNotEmpty ? uri.pathSegments.last : "";
|
||||
if (name.isEmpty && uri.pathSegments.length > 1) {
|
||||
name = uri.pathSegments[uri.pathSegments.length - 2];
|
||||
} else if (name.isEmpty) {
|
||||
name = uri.host;
|
||||
}
|
||||
|
||||
_bloc.add(
|
||||
MobileRowDetailEvent.addCover(
|
||||
RowCoverPB(
|
||||
data: url,
|
||||
uploadType: FileUploadTypePB.NetworkFile,
|
||||
coverType: CoverTypePB.FileCover,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RowDetailFab extends StatelessWidget {
|
||||
@@ -322,91 +410,129 @@ class MobileRowDetailPageContentState
|
||||
rowController: rowController,
|
||||
),
|
||||
child: BlocBuilder<RowDetailBloc, RowDetailState>(
|
||||
builder: (context, rowDetailState) {
|
||||
return Column(
|
||||
children: [
|
||||
BlocProvider<RowBannerBloc>(
|
||||
create: (context) => RowBannerBloc(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
rowMeta: rowController.rowMeta,
|
||||
)..add(const RowBannerEvent.initial()),
|
||||
child: BlocConsumer<RowBannerBloc, RowBannerState>(
|
||||
listener: (context, state) {
|
||||
if (state.primaryField == null) {
|
||||
return;
|
||||
}
|
||||
primaryFieldId.value = state.primaryField!.id;
|
||||
},
|
||||
builder: (context, state) {
|
||||
if (state.primaryField == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: cellBuilder.buildCustom(
|
||||
CellContext(
|
||||
rowId: rowController.rowId,
|
||||
fieldId: state.primaryField!.id,
|
||||
),
|
||||
skinMap: EditableCellSkinMap(
|
||||
textSkin: _TitleSkin(),
|
||||
),
|
||||
builder: (context, rowDetailState) => Column(
|
||||
children: [
|
||||
if (rowDetailState.rowMeta.cover.data.isNotEmpty) ...[
|
||||
GestureDetector(
|
||||
onTap: () => showMobileBottomSheet(
|
||||
context,
|
||||
backgroundColor: AFThemeExtension.of(context).background,
|
||||
showDragHandle: true,
|
||||
builder: (_) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
MobileQuickActionButton(
|
||||
onTap: () {
|
||||
context
|
||||
..pop()
|
||||
..read<RowDetailBloc>()
|
||||
.add(const RowDetailEvent.removeCover());
|
||||
},
|
||||
text: LocaleKeys.button_delete.tr(),
|
||||
textColor: Theme.of(context).colorScheme.error,
|
||||
icon: FlowySvgs.trash_s,
|
||||
iconColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
);
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.only(top: 9, bottom: 100),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: MobileRowPropertyList(
|
||||
databaseController: widget.databaseController,
|
||||
cellBuilder: cellBuilder,
|
||||
),
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(6, 6, 16, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (rowDetailState.numHiddenFields != 0) ...[
|
||||
const ToggleHiddenFieldsVisibilityButton(),
|
||||
],
|
||||
const VSpace(8.0),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: primaryFieldId,
|
||||
builder: (context, primaryFieldId, child) {
|
||||
if (primaryFieldId.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return OpenRowPageButton(
|
||||
databaseController: widget.databaseController,
|
||||
cellContext: CellContext(
|
||||
rowId: rowController.rowId,
|
||||
fieldId: primaryFieldId,
|
||||
),
|
||||
documentId: rowController.rowMeta.documentId,
|
||||
);
|
||||
},
|
||||
),
|
||||
MobileRowDetailCreateFieldButton(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: AFImage(
|
||||
url: rowDetailState.rowMeta.cover.data,
|
||||
uploadType: widget.rowMeta.cover.uploadType,
|
||||
userProfile:
|
||||
context.read<MobileRowDetailBloc>().userProfile,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
BlocProvider<RowBannerBloc>(
|
||||
create: (context) => RowBannerBloc(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
rowMeta: rowController.rowMeta,
|
||||
)..add(const RowBannerEvent.initial()),
|
||||
child: BlocConsumer<RowBannerBloc, RowBannerState>(
|
||||
listener: (context, state) {
|
||||
if (state.primaryField == null) {
|
||||
return;
|
||||
}
|
||||
primaryFieldId.value = state.primaryField!.id;
|
||||
},
|
||||
builder: (context, state) {
|
||||
if (state.primaryField == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: cellBuilder.buildCustom(
|
||||
CellContext(
|
||||
rowId: rowController.rowId,
|
||||
fieldId: state.primaryField!.id,
|
||||
),
|
||||
skinMap: EditableCellSkinMap(textSkin: _TitleSkin()),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.only(top: 9, bottom: 100),
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: MobileRowPropertyList(
|
||||
databaseController: widget.databaseController,
|
||||
cellBuilder: cellBuilder,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(6, 6, 16, 0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (rowDetailState.numHiddenFields != 0) ...[
|
||||
const ToggleHiddenFieldsVisibilityButton(),
|
||||
],
|
||||
const VSpace(8.0),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: primaryFieldId,
|
||||
builder: (context, primaryFieldId, child) {
|
||||
if (primaryFieldId.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return OpenRowPageButton(
|
||||
databaseController: widget.databaseController,
|
||||
cellContext: CellContext(
|
||||
rowId: rowController.rowId,
|
||||
fieldId: primaryFieldId,
|
||||
),
|
||||
documentId: rowController.rowMeta.documentId,
|
||||
);
|
||||
},
|
||||
),
|
||||
MobileRowDetailCreateFieldButton(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -429,7 +555,9 @@ class _TitleSkin extends IEditableTextCellSkin {
|
||||
fontSize: 23,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
onChanged: (text) => bloc.add(TextCellEvent.updateText(text)),
|
||||
onEditingComplete: () {
|
||||
bloc.add(TextCellEvent.updateText(textEditingController.text));
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 9),
|
||||
border: InputBorder.none,
|
||||
|
||||
+4
-8
@@ -1,13 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/database_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/grid/application/row/row_detail_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/desktop_field_cell.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class MobileRowPropertyList extends StatelessWidget {
|
||||
@@ -77,10 +75,8 @@ class _PropertyCellState extends State<_PropertyCell> {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
FlowySvg(
|
||||
fieldInfo.fieldType.svgData,
|
||||
color: Theme.of(context).hintColor,
|
||||
size: const Size.square(16),
|
||||
FieldIcon(
|
||||
fieldInfo: fieldInfo,
|
||||
),
|
||||
const HSpace(6),
|
||||
Expanded(
|
||||
|
||||
+5
-5
@@ -11,13 +11,13 @@ class OptionTextField extends StatelessWidget {
|
||||
const OptionTextField({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.type,
|
||||
required this.fieldType,
|
||||
required this.onTextChanged,
|
||||
required this.onFieldTypeChanged,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final FieldType type;
|
||||
final FieldType fieldType;
|
||||
final void Function(String value) onTextChanged;
|
||||
final void Function(FieldType value) onFieldTypeChanged;
|
||||
|
||||
@@ -43,12 +43,12 @@ class OptionTextField extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? type.mobileIconBackgroundColor
|
||||
: type.mobileIconBackgroundColorDark,
|
||||
? fieldType.mobileIconBackgroundColor
|
||||
: fieldType.mobileIconBackgroundColorDark,
|
||||
),
|
||||
child: Center(
|
||||
child: FlowySvg(
|
||||
type.svgData,
|
||||
fieldType.svgData,
|
||||
size: const Size.square(22),
|
||||
),
|
||||
),
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ class _OpenRowPageButtonState extends State<OpenRowPageButton> {
|
||||
),
|
||||
onPressed: () {
|
||||
final name = state.content;
|
||||
_openRowPage(context, name);
|
||||
_openRowPage(context, name ?? "");
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
+23
-47
@@ -1,16 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/card/card.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/card/card_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_builder.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/card_cell_style_maps/mobile_board_card_cell_style.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||
|
||||
class MobileCardContent extends StatelessWidget {
|
||||
const MobileCardContent({
|
||||
@@ -19,57 +14,38 @@ class MobileCardContent extends StatelessWidget {
|
||||
required this.cellBuilder,
|
||||
required this.cells,
|
||||
required this.styleConfiguration,
|
||||
required this.userProfile,
|
||||
});
|
||||
|
||||
final RowMetaPB rowMeta;
|
||||
final CardCellBuilder cellBuilder;
|
||||
final List<CellMeta> cells;
|
||||
final RowCardStyleConfiguration styleConfiguration;
|
||||
final UserProfilePB? userProfile;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final attachmentCount = rowMeta.attachmentCount.toInt();
|
||||
|
||||
return Padding(
|
||||
padding: styleConfiguration.cardPadding,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...cells.map(
|
||||
(cellMeta) {
|
||||
return cellBuilder.build(
|
||||
cellContext: cellMeta.cellContext(),
|
||||
styleMap: mobileBoardCardCellStyleMap(context),
|
||||
hasNotes: !rowMeta.isDocumentEmpty,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (attachmentCount > 0) ...[
|
||||
const VSpace(4),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
const FlowySvg(
|
||||
FlowySvgs.media_s,
|
||||
size: Size.square(12),
|
||||
),
|
||||
const HSpace(6),
|
||||
Flexible(
|
||||
child: FlowyText.regular(
|
||||
LocaleKeys.grid_media_attachmentsHint
|
||||
.tr(args: ['$attachmentCount']),
|
||||
fontSize: 12,
|
||||
color: AFThemeExtension.of(context).secondaryTextColor,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (rowMeta.cover.data.isNotEmpty) ...[
|
||||
CardCover(cover: rowMeta.cover, userProfile: userProfile),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: styleConfiguration.cardPadding,
|
||||
child: Column(
|
||||
children: [
|
||||
...cells.map(
|
||||
(cellMeta) => cellBuilder.build(
|
||||
cellContext: cellMeta.cellContext(),
|
||||
styleMap: mobileBoardCardCellStyleMap(context),
|
||||
hasNotes: !rowMeta.isDocumentEmpty,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
+1
@@ -37,6 +37,7 @@ class _MobileNewPropertyScreenState extends State<MobileNewPropertyScreen> {
|
||||
final type = widget.fieldType ?? FieldType.RichText;
|
||||
optionValues = FieldOptionValues(
|
||||
type: type,
|
||||
icon: "",
|
||||
name: type.i18n,
|
||||
);
|
||||
}
|
||||
|
||||
+4
-5
@@ -1,10 +1,9 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/base/app_bar/app_bar_actions.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_option_tile.dart';
|
||||
import 'package:appflowy/plugins/base/drag_handler.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_info.dart';
|
||||
import 'package:appflowy/util/field_type_extension.dart';
|
||||
import 'package:appflowy/plugins/database/grid/presentation/widgets/header/desktop_field_cell.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
@@ -132,9 +131,9 @@ class _FieldButton extends StatelessWidget {
|
||||
return FlowyOptionTile.checkbox(
|
||||
text: field.name,
|
||||
isSelected: isSelected,
|
||||
leftIcon: FlowySvg(
|
||||
field.fieldType.svgData,
|
||||
size: const Size.square(20),
|
||||
leftIcon: FieldIcon(
|
||||
fieldInfo: field,
|
||||
dimension: 20,
|
||||
),
|
||||
showTopBorder: showTopBorder,
|
||||
onTap: () => onSelect(field.id),
|
||||
|
||||
+4
-1
@@ -35,6 +35,7 @@ class FieldOptionValues {
|
||||
FieldOptionValues({
|
||||
required this.type,
|
||||
required this.name,
|
||||
required this.icon,
|
||||
this.dateFormat,
|
||||
this.timeFormat,
|
||||
this.includeTime,
|
||||
@@ -48,6 +49,7 @@ class FieldOptionValues {
|
||||
return FieldOptionValues(
|
||||
type: fieldType,
|
||||
name: field.name,
|
||||
icon: field.icon,
|
||||
numberFormat: fieldType == FieldType.Number
|
||||
? NumberTypeOptionPB.fromBuffer(buffer).format
|
||||
: null,
|
||||
@@ -83,6 +85,7 @@ class FieldOptionValues {
|
||||
|
||||
FieldType type;
|
||||
String name;
|
||||
String icon;
|
||||
|
||||
// FieldType.DateTime
|
||||
// FieldType.LastEditedTime
|
||||
@@ -221,7 +224,7 @@ class _MobileFieldEditorState extends State<MobileFieldEditor> {
|
||||
const _Divider(),
|
||||
OptionTextField(
|
||||
controller: controller,
|
||||
type: values.type,
|
||||
fieldType: values.type,
|
||||
onTextChanged: (value) {
|
||||
isFieldNameChanged = true;
|
||||
_updateOptionValues(name: value);
|
||||
|
||||
Loaded 100 of 1063 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user