Compare commits

..
4040 changed files with 56796 additions and 191765 deletions

No files matched your search

+12 -17
View File
@@ -58,24 +58,19 @@ runs:
- name: Install prerequisites
working-directory: frontend
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
if [ "$RUNNER_OS" == "Linux" ]; then
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
elif [ "$RUNNER_OS" == "Windows" ]; then
vcpkg integrate install
elif [ "$RUNNER_OS" == "macOS" ]; then
echo 'do nothing'
fi
cargo make appflowy-flutter-deps-tools
shell: bash
- name: Build AppFlowy
working-directory: frontend
@@ -99,4 +94,4 @@ runs:
- uses: actions/upload-artifact@v4
with:
name: ${{ github.run_id }}-${{ matrix.os }}
path: appflowy_flutter.tar.gz
path: appflowy_flutter.tar.gz
@@ -52,7 +52,7 @@ runs:
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev network-manager libmpv-dev mpv
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev network-manager
shell: bash
- name: Enable Flutter Desktop
+2 -2
View File
@@ -19,8 +19,8 @@
# env:
# CARGO_TERM_COLOR: always
# FLUTTER_VERSION: "3.22.0"
# RUST_TOOLCHAIN: "1.77.2"
# FLUTTER_VERSION: "3.19.0"
# RUST_TOOLCHAIN: "1.75"
# CARGO_MAKE_VERSION: "0.36.6"
# concurrency:
+28
View File
@@ -0,0 +1,28 @@
name: Build Bot
on:
issue_comment:
types: [created]
jobs:
dispatch_slash_command:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
# get build name from pubspec.yaml
- name: Get build version
working-directory: frontend/appflowy_flutter
id: get_build_name
run: |
echo "fetching version from pubspec.yaml..."
echo "build_name=$(grep 'version: ' pubspec.yaml | awk '{print $2}')" >> $GITHUB_OUTPUT
- uses: peter-evans/slash-command-dispatch@v4
with:
token: ${{ secrets.PAT }}
commands: build
static-args: |
ref=refs/pull/${{ github.event.issue.number }}/head
build_name=${{ steps.get_build_name.outputs.build_name }}
+72
View File
@@ -0,0 +1,72 @@
name: Deploy Web (Test)
on:
push:
branches:
- build/test
env:
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
env:
SSH_PRIVATE_KEY: ${{ secrets.WEB_TEST_SSH_PRIVATE_KEY }}
REMOTE_HOST: ${{ secrets.WEB_TEST_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.WEB_TEST_REMOTE_USER }}
SSL_CERTIFICATE: ${{ secrets.WEB_TEST_SSL_CERTIFICATE }}
SSL_CERTIFICATE_KEY: ${{ secrets.WEB_TEST_SSL_CERTIFICATE_KEY }}
ENV_FILE: test.env
steps:
- uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
- name: Node_modules cache
uses: actions/cache@v2
with:
path: frontend/appflowy_web_app/node_modules
key: node-modules-${{ runner.os }}
- name: install frontend dependencies
working-directory: frontend/appflowy_web_app
run: |
pnpm install
- name: copy env file
working-directory: frontend/appflowy_web_app
run: |
cp ${{ env.ENV_FILE }} .env
- name: test and lint
working-directory: frontend/appflowy_web_app
run: |
pnpm run lint
- name: build
working-directory: frontend/appflowy_web_app
run: |
pnpm run build
- name: generate SSL certificate
run: |
echo "${{ env.SSL_CERTIFICATE }}" > nginx-signed.crt
echo "${{ env.SSL_CERTIFICATE_KEY }}" > nginx-signed.key
- name: Deploy to EC2
uses: easingthemes/ssh-deploy@main
with:
SSH_PRIVATE_KEY: ${{ env.SSH_PRIVATE_KEY }}
ARGS: "-rlgoDzvc -i"
SOURCE: "frontend/appflowy_web_app/dist frontend/appflowy_web_app/Dockerfile frontend/appflowy_web_app/nginx.conf frontend/appflowy_web_app/.env nginx-signed.crt nginx-signed.key"
REMOTE_HOST: ${{ env.REMOTE_HOST }}
REMOTE_USER: ${{ env.REMOTE_USER }}
EXCLUDE: "frontend/appflowy_web_app/dist/, frontend/appflowy_web_app/node_modules/"
SCRIPT_AFTER: |
docker build -t appflowy-web-app .
docker rm -f appflowy-web-app || true
docker run -d -p 80:80 -p 443:443 --name appflowy-web-app appflowy-web-app
+31 -27
View File
@@ -2,10 +2,18 @@ name: Docker-CI
on:
push:
branches: ["main", "release/*"]
branches:
- main
- release/*
paths:
- frontend/**
pull_request:
branches: ["main", "release/*"]
workflow_dispatch:
branches:
- main
- release/*
paths:
- frontend/**
types: [opened, synchronize, reopened, unlocked, ready_for_review]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -19,29 +27,25 @@ jobs:
- name: Checkout source code
uses: actions/checkout@v4
- 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: 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: Build the app
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
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
+28 -70
View File
@@ -25,9 +25,9 @@ on:
env:
CARGO_TERM_COLOR: always
FLUTTER_VERSION: "3.22.2"
RUST_TOOLCHAIN: "1.80.1"
CARGO_MAKE_VERSION: "0.37.18"
FLUTTER_VERSION: "3.19.0"
RUST_TOOLCHAIN: "1.75"
CARGO_MAKE_VERSION: "0.36.6"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -39,7 +39,7 @@ jobs:
strategy:
fail-fast: true
matrix:
os: [ ubuntu-latest ]
os: [ubuntu-latest]
include:
- os: ubuntu-latest
flutter_profile: development-linux-x86_64
@@ -73,9 +73,9 @@ jobs:
strategy:
fail-fast: true
matrix:
os: [ windows-2019 ]
os: [windows-latest]
include:
- os: windows-2019
- os: windows-latest
flutter_profile: development-windows-x86
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
@@ -89,7 +89,6 @@ jobs:
with:
os: ${{ matrix.os }}
flutter_version: ${{ env.FLUTTER_VERSION }}
DISABLE_CI_TEST_LOG: "true"
rust_toolchain: ${{ env.RUST_TOOLCHAIN }}
cargo_make_version: ${{ env.CARGO_MAKE_VERSION }}
rust_target: ${{ matrix.target }}
@@ -100,7 +99,7 @@ jobs:
strategy:
fail-fast: true
matrix:
os: [ macos-latest ]
os: [macos-latest]
include:
- os: macos-latest
flutter_profile: development-mac-x86_64
@@ -122,12 +121,12 @@ jobs:
flutter_profile: ${{ matrix.flutter_profile }}
unit_test:
needs: [ prepare-linux ]
needs: [prepare-linux]
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
os: [ubuntu-latest]
include:
- os: ubuntu-latest
flutter_profile: development-linux-x86_64
@@ -173,7 +172,7 @@ jobs:
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev libmpv-dev mpv
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
fi
shell: bash
@@ -201,9 +200,6 @@ jobs:
run: cargo make pub_get
- name: Run Flutter unit tests
env:
DISABLE_EVENT_LOG: true
DISABLE_CI_TEST_LOG: "true"
working-directory: frontend
run: |
if [ "$RUNNER_OS" == "macOS" ]; then
@@ -216,11 +212,11 @@ jobs:
shell: bash
cloud_integration_test:
needs: [ prepare-linux ]
needs: [prepare-linux]
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
os: [ubuntu-latest]
include:
- os: ubuntu-latest
flutter_profile: development-linux-x86_64
@@ -241,35 +237,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.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
echo "AppFlowy-Cloud container is not running. Pulling and starting the container..."
docker compose pull
docker compose up -d
echo "Waiting for the container to be ready..."
sleep 10
else
running_image=$(docker inspect --format='{{index .Config.Image}}' "$container_id")
if [ "$running_image" != "appflowy-cloud:$APPFLOWY_CLOUD_VERSION" ]; then
echo "AppFlowy-Cloud is running with an incorrect version. Pulling the correct version..."
docker compose pull
docker compose up -d
echo "Waiting for the container to be ready..."
sleep 10
else
echo "AppFlowy-Cloud is running with the correct version."
fi
fi
docker compose down -v --remove-orphans
docker compose pull
docker compose up -d
sleep 10
- name: Checkout source code
uses: actions/checkout@v4
@@ -292,7 +268,7 @@ jobs:
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo wget -qO /etc/apt/sources.list.d/dart_stable.list https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list
sudo apt-get update
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev libmpv-dev mpv
sudo apt-get install -y dart curl build-essential libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev keybinder-3.0 libnotify-dev
shell: bash
- name: Enable Flutter Desktop
@@ -320,31 +296,25 @@ 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/desktop/cloud/cloud_runner.dart -d Linux --coverage
flutter test integration_test/cloud/cloud_runner.dart -d Linux --coverage
shell: bash
# split the integration tests into different machines to minimize the time
integration_test_1:
needs: [ prepare-linux ]
needs: [prepare-linux]
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
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
uses: actions/checkout@v4
- name: Install video dependency
run: |
sudo apt-get update
sudo apt-get -y install libmpv-dev mpv
shell: bash
- name: Flutter Integration Test 1
uses: ./.github/actions/flutter_integration_test
with:
@@ -355,26 +325,20 @@ jobs:
rust_target: ${{ matrix.target }}
integration_test_2:
needs: [ prepare-linux ]
needs: [prepare-linux]
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
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
uses: actions/checkout@v4
- name: Install video dependency
run: |
sudo apt-get update
sudo apt-get -y install libmpv-dev mpv
shell: bash
- name: Flutter Integration Test 2
uses: ./.github/actions/flutter_integration_test
with:
@@ -385,26 +349,20 @@ jobs:
rust_target: ${{ matrix.target }}
integration_test_3:
needs: [ prepare-linux ]
needs: [prepare-linux]
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
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
uses: actions/checkout@v4
- name: Install video dependency
run: |
sudo apt-get update
sudo apt-get -y install libmpv-dev mpv
shell: bash
- name: Flutter Integration Test 3
uses: ./.github/actions/flutter_integration_test
with:
@@ -412,4 +370,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 }}
+16 -41
View File
@@ -8,7 +8,6 @@ on:
- ".github/workflows/mobile_ci.yaml"
- "frontend/**"
- "!frontend/appflowy_tauri/**"
- "!frontend/appflowy_web_app/**"
pull_request:
branches:
@@ -17,46 +16,30 @@ on:
- ".github/workflows/mobile_ci.yaml"
- "frontend/**"
- "!frontend/appflowy_tauri/**"
- "!frontend/appflowy_web_app/**"
env:
FLUTTER_VERSION: "3.22.0"
RUST_TOOLCHAIN: "1.80.1"
FLUTTER_VERSION: "3.19.0"
RUST_TOOLCHAIN: "1.75"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-self-hosted:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: self-hosted
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Build AppFlowy
working-directory: frontend
run: |
cargo make --profile development-ios-arm64-sim appflowy-core-dev-ios
cargo make --profile development-ios-arm64-sim code_generation
- uses: futureware-tech/simulator-action@v3
id: simulator-action
with:
model: "iPhone 15"
shutdown_after_job: false
build-macos:
if: github.event.pull_request.head.repo.full_name != github.repository
runs-on: macos-13
build:
if: github.event.pull_request.draft != true
strategy:
fail-fast: true
matrix:
os: [macos-14]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
@@ -64,7 +47,8 @@ jobs:
override: true
profile: minimal
- name: Install Flutter
- name: Install flutter
id: flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
@@ -73,13 +57,13 @@ jobs:
- uses: Swatinem/rust-cache@v2
with:
prefix-key: macos-latest
prefix-key: ${{ matrix.os }}
workspaces: |
frontend/rust-lib
- uses: davidB/rust-cargo-make@v1
with:
version: "0.37.15"
version: "0.36.6"
- name: Install prerequisites
working-directory: frontend
@@ -99,19 +83,10 @@ jobs:
- uses: futureware-tech/simulator-action@v3
id: simulator-action
with:
model: "iPhone 15"
model: 'iPhone 15'
shutdown_after_job: false
# - name: Run AppFlowy on simulator
# working-directory: frontend/appflowy_flutter
# run: |
# flutter run -d ${{ steps.simulator-action.outputs.udid }} &
# pid=$!
# sleep 500
# kill $pid
# continue-on-error: true
# enable it again if the 12 mins timeout is fixed
# - name: Run integration tests
# working-directory: frontend/appflowy_flutter
# run: flutter test integration_test/runner.dart -d ${{ steps.simulator-action.outputs.udid }}
# run: flutter test integration_test/runner.dart -d ${{ steps.simulator-action.outputs.udid }}
+6 -24
View File
@@ -6,8 +6,8 @@ on:
- "*"
env:
FLUTTER_VERSION: "3.22.0"
RUST_TOOLCHAIN: "1.77.2"
FLUTTER_VERSION: "3.19.0"
RUST_TOOLCHAIN: "1.75"
jobs:
create-release:
@@ -135,7 +135,7 @@ jobs:
fail-fast: false
matrix:
job:
- { target: x86_64-apple-darwin, os: macos-12, extra-build-args: "" }
- { target: x86_64-apple-darwin, os: macos-11, extra-build-args: "" }
steps:
- name: Checkout source code
uses: actions/checkout@v4
@@ -233,7 +233,7 @@ jobs:
job:
- {
targets: "aarch64-apple-darwin,x86_64-apple-darwin",
os: macos-latest,
os: macos-11,
extra-build-args: "",
}
steps:
@@ -367,8 +367,8 @@ jobs:
sudo wget -qO /etc/apt/trusted.gpg.d/dart_linux_signing_key.asc https://dl-ssl.google.com/linux/linux_signing_key.pub
sudo apt-get update
sudo apt-get install -y build-essential libsqlite3-dev libssl-dev clang cmake ninja-build pkg-config libgtk-3-dev
sudo apt-get install keybinder-3.0
sudo apt-get install -y alien libnotify-dev libmpv-dev mpv
sudo apt-get install keybinder-3.0 libnotify-dev
sudo apt-get -y install alien
source $HOME/.cargo/env
cargo install --force cargo-make
cargo install --force duckscript_cli
@@ -479,24 +479,6 @@ jobs:
cache-from: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache
cache-to: type=registry,ref=${{ secrets.DOCKER_HUB_USERNAME }}/af_build_cache:buildcache,mode=max
notify-failure:
runs-on: ubuntu-latest
needs:
- build-for-macOS-x86_64
- build-for-windows
- build-for-linux
if: failure()
steps:
- uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
🔴🔴🔴Workflow ${{ github.workflow }} in repository ${{ github.repository }} was failed 🔴🔴🔴.
fields: repo,message,author,eventName,ref,workflow
env:
SLACK_WEBHOOK_URL: ${{ secrets.RELEASE_SLACK_WEBHOOK }}
if: always()
notify-discord:
runs-on: ubuntu-latest
needs:
+31 -111
View File
@@ -8,7 +8,6 @@ on:
- "release/*"
paths:
- "frontend/rust-lib/**"
- ".github/workflows/rust_ci.yaml"
pull_request:
branches:
@@ -20,101 +19,49 @@ on:
env:
CARGO_TERM_COLOR: always
RUST_TOOLCHAIN: "1.77.2"
RUST_TOOLCHAIN: "1.75"
jobs:
self-hosted-job:
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: self-hosted
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Checkout Appflowy Cloud
uses: actions/checkout@v4
with:
repository: AppFlowy-IO/AppFlowy-Cloud
path: AppFlowy-Cloud
- name: Prepare Appflowy Cloud env
working-directory: AppFlowy-Cloud
run: |
cp deploy.env .env
sed -i '' 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i '' 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
- name: 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
echo "AppFlowy-Cloud container is not running. Pulling and starting the container..."
docker compose pull
docker compose up -d
echo "Waiting for the container to be ready..."
sleep 10
else
running_image=$(docker inspect --format='{{index .Config.Image}}' "$container_id")
if [ "$running_image" != "appflowy-cloud:$APPFLOWY_CLOUD_VERSION" ]; then
echo "AppFlowy-Cloud is running with an incorrect version. Pulling the correct version..."
docker compose pull
docker compose up -d
echo "Waiting for the container to be ready..."
sleep 10
else
echo "AppFlowy-Cloud is running with the correct version."
fi
fi
- name: Run rust-lib tests
working-directory: frontend/rust-lib
env:
RUST_LOG: info
RUST_BACKTRACE: 1
af_cloud_test_base_url: http://localhost
af_cloud_test_ws_url: ws://localhost/ws/v1
af_cloud_test_gotrue_url: http://localhost/gotrue
run: |
DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="dart"
- name: rustfmt rust-lib
run: cargo fmt --all -- --check
working-directory: frontend/rust-lib/
- name: clippy rust-lib
run: cargo clippy --all-targets -- -D warnings
working-directory: frontend/rust-lib
ubuntu-job:
if: github.event.pull_request.head.repo.full_name != github.repository
test-on-ubuntu:
runs-on: ubuntu-latest
steps:
- name: Maximize build space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
sudo docker image prune --all --force
# - name: Maximize build space
# uses: easimon/maximize-build-space@master
# with:
# root-reserve-mb: 2048
# swap-size-mb: 1024
# remove-dotnet: 'true'
# # the following step is required to avoid running out of space
# - name: Maximize build space
# run: |
# sudo rm -rf /usr/share/dotnet
# sudo rm -rf /opt/ghc
# sudo rm -rf "/usr/local/share/boost"
# sudo rm -rf "$AGENT_TOOLSDIRECTORY"
# sudo docker image prune --all --force
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
override: true
components: rustfmt, clippy
profile: minimal
- name: Install prerequisites
working-directory: frontend
run: |
cargo install --force cargo-make
cargo install --force duckscript_cli
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ runner.os }}
cache-on-failure: true
prefix-key: "ubuntu-latest"
workspaces: |
frontend/rust-lib
@@ -127,37 +74,16 @@ jobs:
- name: Prepare appflowy cloud env
working-directory: AppFlowy-Cloud
run: |
# log level
cp deploy.env .env
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i 's|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
- name: Run Docker-Compose
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
echo "AppFlowy-Cloud container is not running. Pulling and starting the container..."
docker compose pull
docker compose up -d
echo "Waiting for the container to be ready..."
sleep 10
else
running_image=$(docker inspect --format='{{index .Config.Image}}' "$container_id")
if [ "$running_image" != "appflowy-cloud:$APPFLOWY_CLOUD_VERSION" ]; then
echo "AppFlowy-Cloud is running with an incorrect version. Pulling the correct version..."
docker compose pull
docker compose up -d
echo "Waiting for the container to be ready..."
sleep 10
else
echo "AppFlowy-Cloud is running with the correct version."
fi
fi
docker pull appflowyinc/appflowy_cloud:latest
docker compose up -d
- name: Run rust-lib tests
working-directory: frontend/rust-lib
@@ -168,7 +94,7 @@ jobs:
af_cloud_test_ws_url: ws://localhost/ws/v1
af_cloud_test_gotrue_url: http://localhost/gotrue
run: |
DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="dart"
DISABLE_CI_TEST_LOG="true" cargo test --no-default-features --features="rev-sqlite,dart" -- --nocapture
- name: rustfmt rust-lib
run: cargo fmt --all -- --check
@@ -178,12 +104,6 @@ jobs:
run: cargo clippy --all-targets -- -D warnings
working-directory: frontend/rust-lib
- name: "Debug: show Appflowy-Cloud container logs"
if: failure()
working-directory: AppFlowy-Cloud
run: |
docker compose logs appflowy_cloud
- name: Clean up Docker images
run: |
docker image prune -af
+2 -2
View File
@@ -10,8 +10,8 @@ on:
env:
CARGO_TERM_COLOR: always
FLUTTER_VERSION: "3.22.0"
RUST_TOOLCHAIN: "1.77.2"
FLUTTER_VERSION: "3.19.0"
RUST_TOOLCHAIN: "1.75"
jobs:
tests:
+30 -41
View File
@@ -1,5 +1,4 @@
name: Tauri-CI
name: Tauri2-CI
on:
pull_request:
paths:
@@ -11,48 +10,29 @@ on:
env:
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
RUST_TOOLCHAIN: "1.77.2"
CARGO_MAKE_VERSION: "0.36.6"
CI: true
RUST_TOOLCHAIN: "1.75"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
# tauri-build-self-hosted:
# if: github.event.pull_request.head.repo.full_name == github.repository
# runs-on: self-hosted
#
# steps:
# - uses: actions/checkout@v4
# - name: install frontend dependencies
# working-directory: frontend/appflowy_web_app
# run: |
# mkdir dist
# pnpm install
# cd src-tauri && cargo build
#
# - name: test and lint
# working-directory: frontend/appflowy_web_app
# run: |
# pnpm run lint:tauri
#
# - uses: tauri-apps/tauri-action@v0
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# with:
# tauriScript: pnpm tauri
# projectPath: frontend/appflowy_web_app
# args: "--debug"
tauri-build:
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
platform: [ ubuntu-20.04 ]
tauri-build-ubuntu:
#if: github.event.pull_request.head.repo.full_name != github.repository
runs-on: ubuntu-20.04
runs-on: ${{ matrix.platform }}
env:
CI: true
steps:
- uses: actions/checkout@v4
- name: Maximize build space
- name: Maximize build space (ubuntu only)
if: matrix.platform == 'ubuntu-20.04'
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
@@ -81,27 +61,36 @@ jobs:
override: true
profile: minimal
- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./frontend/appflowy_web_app/src-tauri -> target"
- name: Node_modules cache
uses: actions/cache@v2
with:
path: frontend/appflowy_web_app/node_modules
key: node-modules-${{ runner.os }}
- name: install dependencies
- name: install dependencies (windows only)
if: matrix.platform == 'windows-latest'
working-directory: frontend
run: |
cargo install --force duckscript_cli
vcpkg integrate install
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-20.04'
working-directory: frontend
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- uses: taiki-e/install-action@v2
with:
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
- name: install tauri deps tools
- name: install cargo-make
working-directory: frontend
run: |
cargo install --force cargo-make
cargo make appflowy-tauri-deps-tools
shell: bash
- name: install frontend dependencies
working-directory: frontend/appflowy_web_app
+8 -5
View File
@@ -1,13 +1,16 @@
name: Tauri-CI
on:
push:
branches:
- build/tauri
pull_request:
paths:
- ".github/workflows/tauri_ci.yaml"
- "frontend/rust-lib/**"
- "frontend/appflowy_tauri/**"
- "frontend/resources/**"
env:
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
RUST_TOOLCHAIN: "1.77.2"
RUST_TOOLCHAIN: "1.75"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -19,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [ ubuntu-20.04 ]
platform: [ubuntu-20.04]
runs-on: ${{ matrix.platform }}
+1 -1
View File
@@ -14,7 +14,7 @@ on:
env:
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
RUST_TOOLCHAIN: "1.77.2"
RUST_TOOLCHAIN: "1.75"
jobs:
+4 -5
View File
@@ -1,4 +1,4 @@
name: Web-CI
name: Web2-CI
on:
pull_request:
paths:
@@ -17,14 +17,14 @@ jobs:
strategy:
fail-fast: false
matrix:
platform: [ ubuntu-20.04 ]
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Maximize build space (ubuntu only)
if: matrix.platform == 'ubuntu-20.04'
if: matrix.platform == 'ubuntu-latest'
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
@@ -52,11 +52,10 @@ jobs:
working-directory: frontend/appflowy_web_app
run: |
pnpm install
- name: Run lint check
- name: test and lint
working-directory: frontend/appflowy_web_app
run: |
pnpm run lint
- name: build and analyze
working-directory: frontend/appflowy_web_app
run: |
+83
View File
@@ -0,0 +1,83 @@
name: WEB-CI
on:
workflow_dispatch:
inputs:
build:
description: 'Build the web app'
required: true
default: 'true'
env:
CARGO_TERM_COLOR: always
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
RUST_TOOLCHAIN: "1.75"
CARGO_MAKE_VERSION: "0.36.6"
jobs:
web-build:
if: github.event.pull_request.draft != true
strategy:
fail-fast: false
matrix:
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: setup node
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Cache Rust Dependencies
uses: Swatinem/rust-cache@v2
with:
key: rust-dependencies-${{ runner.os }}
workspaces: |
frontend/rust-lib
frontend/appflowy_web/appflowy_wasm
# TODO: Can combine caching deps and node_modules in one
# See Glob patterns: https://github.com/actions/toolkit/tree/main/packages/glob
- name: Cache Node.js dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}
- name: Cache node_modules
uses: actions/cache@v4
with:
path: frontend/appflowy_web/node_modules
key: node-modules-${{ runner.os }}
- name: Install Rust toolchain
id: rust_toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
override: true
profile: minimal
- name: Install wasm-pack
run: cargo install wasm-pack
- uses: taiki-e/install-action@v2
with:
tool: cargo-make@${{ env.CARGO_MAKE_VERSION }}
- name: install dependencies
if: matrix.platform == 'ubuntu-latest'
working-directory: frontend
run: |
sudo apt-get update
npm install -g pnpm@${{ env.PNPM_VERSION }}
cargo make install_web_protobuf
- name: Build
working-directory: frontend/appflowy_web
run: |
pnpm install
pnpm run build_release_wasm
-65
View File
@@ -1,65 +0,0 @@
name: Web Code Coverage
on:
pull_request:
paths:
- ".github/workflows/web2_ci.yaml"
- "frontend/appflowy_web_app/**"
- "frontend/resources/**"
env:
NODE_VERSION: "18.16.0"
PNPM_VERSION: "8.5.0"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
test:
if: github.event.pull_request.draft != true
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Maximize build space (ubuntu only)
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
sudo docker image prune --all --force
sudo rm -rf /opt/hostedtoolcache/codeQL
sudo rm -rf ${GITHUB_WORKSPACE}/.git
sudo rm -rf $ANDROID_HOME/ndk
- name: setup node
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- name: setup pnpm
uses: pnpm/action-setup@v2
with:
version: ${{ env.PNPM_VERSION }}
# Install pnpm dependencies, cache them correctly
# and run all Cypress tests
- name: Cypress run
uses: cypress-io/github-action@v6
with:
working-directory: frontend/appflowy_web_app
component: true
build: pnpm run build
start: pnpm run start
browser: chrome
- name: Jest run
working-directory: frontend/appflowy_web_app
run: |
pnpm run test:unit
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
with:
token: cf9245e0-e136-4e21-b0ee-35755fa0c493
files: frontend/appflowy_web_app/coverage/jest/lcov.info,frontend/appflowy_web_app/coverage/cypress/lcov.info
flags: appflowy_web_app
name: frontend/appflowy_web_app
fail_ci_if_error: true
verbose: true
+4 -171
View File
@@ -1,176 +1,9 @@
# Release Notes
## Version 0.7.0 - 19/09/2024
## Version 0.5.5 - 04/22/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
- TBD
### 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'
- Supported Apple Sign-in
- Displayed the page icon next to the row name when the row page contains nested notes
- Enabled Delete Account in Settings
- Included a collapsible navigation menu in your published site
### Bug Fixes
- Fixed the space name color issue in the community themes
- Fixed database filters and sorting issues
- Fixed the issue of not being able to fully display the title on Kanban cards
- Fixed the inability to see the entire text of a checklist item when it's more than one line long
- Fixed hide/unhide buttons in the No Status group
- Fixed the inability to edit group names on Kanban boards
- Made error codes more user-friendly
- Added leading zeros to day and month in date format
## Version 0.6.8 - 22/08/2024
### New Features
- Enabled viewing data inside a database record on mobile.
- Added the ability to invite members to a workspace on mobile.
- Introduced Ask AI in the Home tab on mobile.
- Import CSV files with up to 1,000 rows.
- Convert properties from one type to another while preserving the data.
- Optimized the speed of opening documents and databases.
- Improved syncing performance across devices.
- Added support for a monochrome app icon on Android.
### Bug Fixes
- Removed the Wayland header from the AppImage build.
- Fixed the issue where pasting a web image on mobile failed.
- Corrected the Local AI state when switching between different workspaces.
- Fixed high CPU usage when opening large databases.
## Version 0.6.7 - 13/08/2024
### New Features
- Redesigned the icon picker design on Desktop.
- Redesigned the notification page on Mobile.
### Bug Fixes
- Enhance the toolbar tooltip functionality on Desktop.
- Enhance the slash menu user experience on Desktop.
- Fixed the issue where list style overrides occurred during text pasting.
- Fixed the issue where linking multiple databases in the same document could cause random loss of focus.
## Version 0.6.6 - 30/07/2024
### New Features
- Upgrade your workspace to a premium plan to unlock more features and storage.
- Image galleries and drag-and-drop image support in documents.
### Bug Fixes
- Fix minor UI issues on Desktop and Mobile.
## Version 0.6.5 - 24/07/2024
### New Features
- Publish a Database to the Web
## Version 0.6.4 - 16/07/2024
### New Features
- Enhanced the message style on the AI chat page.
- Added the ability to choose cursor color and selection color from a palette in settings page.
### Bug Fixes
- Optimized the performance for loading recent pages.
- Fixed an issue where the cursor would jump randomly when typing in the document title on mobile.
## Version 0.6.3 - 08/07/2024
### New Features
- Publish a Document to the Web
## Version 0.6.2 - 01/07/2024
### New Features
- Added support for duplicating spaces.
- Added support for moving pages across spaces.
- Undo markdown formatting with `Ctrl + Z` or `Cmd + Z`.
- Improved shortcuts settings UI.
### Bug Fixes
- Fixed unable to zoom in with `Ctrl` and `+` or `Cmd` and `+` on some keyboards.
- Fixed unable to paste nested lists in existing lists.
## Version 0.6.1 - 22/06/2024
### New Features
- Introduced the "Space" feature to help you organize your pages more efficiently.
### Bug Fixes
- Resolved shortcut conflicts on the board page.
- Resolved an issue where underscores could cause the editor to freeze.
## Version 0.6.0 - 19/06/2024
### New Features
- Introduced the "Space" feature to help you organize your pages more efficiently.
### Bug Fixes
- Resolved shortcut conflicts on the board page.
- Resolved an issue where underscores could cause the editor to freeze.
## Version 0.5.9 - 06/06/2024
### New Features
- Revamped the sidebar for both Desktop and Mobile.
- Added support for embedding videos in documents.
- Introduced a hotkey (Cmd/Ctrl + 0) to reset the app scale.
- Supported searching the workspace by page title.
### Bug Fixes
- Fixed the issue preventing the use of Backspace to delete words in Kanban boards.
## Version 0.5.8 - 05/20/2024
### New Features
- Improvement to the Callout block to insert new lines
- New settings page "Manage data" replaced the "Files" page
- New settings page "Workspace" replaced the "Appearance" and "Language" pages
- A custom implementation of a title bar for Windows users
- Added support for selecting Cards in kanban and performing grouped keyboard shortcuts
- Added support for default system font family
- Support for scaling the application up/down using a keyboard shortcut (CMD/CTRL + PLUS/MINUS)
### Bug Fixes
- Resolved and refined the UI on Mobile
- Resolved issue with text editing in database
- Improved appearance of empty text cells in kanban/calendar
- Resolved an issue where a page's more actions (delete, duplicate) did not work properly
- Resolved and inconsistency in padding on get started screen on Desktop
## Version 0.5.7 - 05/10/2024
### Bug Fixes
- Resolved page opening issue on Android.
- Fixed text input inconsistency on Kanban board cards.
## Version 0.5.6 - 05/07/2024
### New Features
- Team collaboration is live! Add members to your workspace to edit and collaborate on pages together.
- Collaborate in real time on the same page with other members. Edits made by others will appear instantly.
- Create multiple workspaces for different kinds of content.
- Customize your entire page on mobile through the Page Style menu with options for layout, font, font size, emoji, and cover image.
- Open a row record as a full page.
### Bug Fixes
- Resolved issue with setting background color for the Simple Table block.
- Adjusted toolbar for various screen sizes.
- Added a request for photo permission before uploading images on mobile.
- Exported creation and last modification timestamps to CSV.
## Version 0.5.5 - 04/24/2024
### New Features
- Improved the display of code blocks with line numbers
- Added support for signing in using Magic Link
### Bug Fixes
- Fixed the database synchronization indicator issue
- Resolved the issue with opening the mentioned page on mobile
- Cleared the collaboration status when the user exits AppFlowy
- TBD
## Version 0.5.4 - 04/08/2024
### New Features
@@ -234,7 +67,7 @@
- Fixed a bug where newly created rows were not being automatically sorted.
- Fixed issues related to deleting a sorting field or sort not removing existing sorts properly.
### Notes
- Windows 7, Windows 8, and iOS 11 are not yet supported due to the upgrade to Flutter 3.22.0.
- Windows 7, Windows 8, and iOS 11 are not yet supported due to the upgrade to Flutter 3.19.0.
## Version 0.4.9 - 02/17/2024
### Bug Fixes
+8 -22
View File
@@ -6,7 +6,7 @@
</h1>
<p align="center">
AppFlowy is the AI workspace where you achieve more without losing control of your data
You are in charge of your data and customizations.
</p>
<p align="center">
@@ -19,34 +19,20 @@ AppFlowy is the AI workspace where you achieve more without losing control of yo
<p align="center">
<a href="https://www.appflowy.io"><b>Website</b></a> •
<a href="https://forum.appflowy.io/"><b>Forum</b></a> •
<a href="https://discord.gg/9Q2xaN37tV"><b>Discord</b></a> •
<a href="https://www.reddit.com/r/AppFlowy"><b>Reddit</b></a> •
<a href="https://twitter.com/appflowy"><b>Twitter</b></a>
</p>
<p align="center"><img src="https://appflowy.io/_next/static/media/tasks.796c753e.png" alt="AppFlowy Kanban Board for To-dos" /></p>
<p align="center"><img src="https://appflowy.io/_next/static/media/Grid.9e30484b.png" alt="AppFlowy Databases for Tasks and Projects" /></p>
<p align="center"><img src="https://appflowy.io/_next/static/media/sites.a8d5b2b9.png" alt="AppFlowy Sites for Beautiful documentation" /></p>
<p align="center"><img src="https://appflowy.io/_next/static/media/ai.e1460982.png" alt="AppFlowy AI" /></p>
<p align="center"><img src="https://appflowy.io/_next/static/media/template.9ea13c3b.png" alt="AppFlowy Templates" /></p>
<br></br>
<p align="center" >
<img src="https://github.com/user-attachments/assets/5841c491-b564-4a26-b9b6-191def430911" alt="Work across devices" width="1040px" /></p>
<p align="center" >
<img src="https://github.com/user-attachments/assets/c2ba6bb8-746c-4743-9393-d008a669be95" alt="Work across devices" width="1040px" /></p>
<p align="center" >
<img src="https://github.com/user-attachments/assets/e83dd1a3-4975-4d0e-91a1-9eb6e0d248cd" alt="Work across devices" width="1040px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/236664610-fc209a97-815e-4716-af07-d94a859d1907.png" alt="AppFlowy Docs & Notes & Wikis" width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/236664628-5def2450-914a-4b2d-b907-92b7476b9863.png" alt="AppFlowy Databases for Tasks and Projects" width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/236664642-22e26c1b-5eae-4635-9aa6-b12ecf1c3c46.png" alt="AppFlowy Kanban Board for To-Dos" width="1000px" /></p>
<p align="center"><img src="https://github.com/AppFlowy-IO/AppFlowy/assets/12026239/6be93d2b-a5c5-48a9-b7cf-c599d5f5140c" alt="AppFlowy Calendars for Plan and Manage Content" width="1000px" /></p>
<p align="center"><img src="https://user-images.githubusercontent.com/12026239/236664657-dc5291f3-67b0-4a43-a818-640e92735deb.png" alt="AppFlowy OpenAI GPT Writers" width="1000px" /></p>
## User Installation
- [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](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)
- [Windows/Mac/Linux](https://docs.appflowy.io/docs/appflowy/install-appflowy/installation-methods/mac-windows-linux-packages)
- [Docker](https://docs.appflowy.io/docs/appflowy/install-appflowy/installation-methods/installing-with-docker)
- [Source](https://docs.appflowy.io/docs/documentation/appflowy/from-source)
## Built With
Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

+2 -1
View File
@@ -13,6 +13,7 @@
"type": "dart",
"env": {
"RUST_LOG": "debug",
"RUST_BACKTRACE": "1"
},
// uncomment the following line to testing performance.
// "flutterMode": "profile",
@@ -137,4 +138,4 @@
"cwd": "${workspaceRoot}/appflowy_tauri/"
},
]
}
}
+40 -39
View File
@@ -1,16 +1,16 @@
#https://github.com/sagiegurari/cargo-make
extend = [
{ path = "scripts/makefile/desktop.toml" },
{ path = "scripts/makefile/mobile.toml" },
{ path = "scripts/makefile/protobuf.toml" },
{ path = "scripts/makefile/tests.toml" },
{ path = "scripts/makefile/docker.toml" },
{ path = "scripts/makefile/env.toml" },
{ path = "scripts/makefile/flutter.toml" },
{ path = "scripts/makefile/tool.toml" },
{ path = "scripts/makefile/tauri.toml" },
{ path = "scripts/makefile/web.toml" },
{ path = "scripts/makefile/desktop.toml" },
{ path = "scripts/makefile/mobile.toml" },
{ path = "scripts/makefile/protobuf.toml" },
{ path = "scripts/makefile/tests.toml" },
{ path = "scripts/makefile/docker.toml" },
{ path = "scripts/makefile/env.toml" },
{ path = "scripts/makefile/flutter.toml" },
{ path = "scripts/makefile/tool.toml" },
{ path = "scripts/makefile/tauri.toml" },
{ path = "scripts/makefile/web.toml" },
]
[config]
@@ -26,8 +26,8 @@ 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.7.1"
FLUTTER_DESKTOP_FEATURES = "dart"
APPFLOWY_VERSION = "0.5.5"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite"
PRODUCT_NAME = "AppFlowy"
MACOSX_DEPLOYMENT_TARGET = "11.0"
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
@@ -50,6 +50,7 @@ APP_ENVIRONMENT = "local"
FLUTTER_FLOWY_SDK_PATH = "appflowy_flutter/packages/appflowy_backend"
TAURI_BACKEND_SERVICE_PATH = "appflowy_tauri/src/services/backend"
WEB_BACKEND_SERVICE_PATH = "appflowy_web/src/services/backend"
WEB_LIB_PATH = "appflowy_web/wasm-libs/af-wasm"
TAURI_APP_BACKEND_SERVICE_PATH = "appflowy_web_app/src/application/services/tauri-services/backend"
# Test default config
TEST_CRATE_TYPE = "cdylib"
@@ -161,7 +162,7 @@ CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Debug"
LIB_EXT = "so"
LINUX_ARCH = "arm64"
FLUTTER_DESKTOP_FEATURES = "dart,openssl_vendored"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[env.production-linux-aarch64]
CARGO_PROFILE = "release"
@@ -173,7 +174,7 @@ FLUTTER_OUTPUT_DIR = "Release"
LIB_EXT = "so"
LINUX_ARCH = "arm64"
APP_ENVIRONMENT = "production"
FLUTTER_DESKTOP_FEATURES = "dart,openssl_vendored"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[env.development-ios-arm64-sim]
BUILD_FLAG = "debug"
@@ -206,7 +207,7 @@ CRATE_TYPE = "cdylib"
FLUTTER_OUTPUT_DIR = "Debug"
LIB_EXT = "so"
PRODUCT_EXT = "apk"
FLUTTER_DESKTOP_FEATURES = "dart,openssl_vendored"
FLUTTER_DESKTOP_FEATURES = "dart,rev-sqlite,openssl_vendored"
[env.production-android]
BUILD_FLAG = "release"
@@ -234,24 +235,24 @@ script_runner = "@shell"
[tasks.setup-crate-type]
private = true
script = [
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} "staticlib" ${CRATE_TYPE}
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
assert ${result}
""",
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} "staticlib" ${CRATE_TYPE}
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
assert ${result}
""",
]
script_runner = "@duckscript"
[tasks.restore-crate-type]
private = true
script = [
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} ${CRATE_TYPE} "staticlib"
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
assert ${result}
""",
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} ${CRATE_TYPE} "staticlib"
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
assert ${result}
""",
]
script_runner = "@duckscript"
@@ -279,24 +280,24 @@ TEST_COMPILE_TARGET = "x86_64-pc-windows-msvc"
[tasks.setup-test-crate-type]
private = true
script = [
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} "staticlib" ${TEST_CRATE_TYPE}
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
assert ${result}
""",
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} "staticlib" ${TEST_CRATE_TYPE}
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
assert ${result}
""",
]
script_runner = "@duckscript"
[tasks.restore-test-crate-type]
private = true
script = [
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} ${TEST_CRATE_TYPE} "staticlib"
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
assert ${result}
""",
"""
toml = readfile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml
val = replace ${toml} ${TEST_CRATE_TYPE} "staticlib"
result = writefile ${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/rust-lib/${CARGO_MAKE_CRATE_NAME}/Cargo.toml ${val}
assert ${result}
""",
]
script_runner = "@duckscript"
@@ -52,8 +52,8 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "io.appflowy.appflowy"
minSdkVersion 29
targetSdkVersion 34
minSdkVersion 23
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
@@ -87,13 +87,6 @@ android {
path "src/main/CMakeLists.txt"
}
}
// only support arm64-v8a
defaultConfig {
ndk {
abiFilters "arm64-v8a"
}
}
}
flutter {
@@ -47,22 +47,9 @@
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Permission to read files from external storage (outside application container).
As of Android 12 this permission no longer has any effect. Instead use the
READ_MEDIA_IMAGES, READ_MEDIA_VIDEO or READM_MEDIA_AUDIO permissions. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Permissions to read media files. -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
<!--
Media access permissions.
Android 13 or higher.
Used for VideoBlock (edia_kit)
-->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
</manifest>
@@ -11,8 +11,6 @@ const uint8_t *sync_event(const uint8_t *input, uintptr_t len);
int32_t set_stream_port(int64_t port);
int32_t set_log_stream_port(int64_t port);
void link_me_please(void);
void rust_log(int64_t level, const char *data);
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/black" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground
android:drawable="@mipmap/ic_launcher_foreground" />
<monochrome
android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon>
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
@@ -1 +0,0 @@
{ "artificial_intelligence": [ { "name": "ai-chip-spark", "keywords": [ "chip", "processor", "artificial", "intelligence", "ai" ], "content": "<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<g clip-path=\"url(#clip0_1068_189544)\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M8.71468 0.0400391C9.1289 0.0400391 9.46468 0.375825 9.46468 0.790039V1.97754H10.4999C11.3283 1.97754 11.9999 2.64911 11.9999 3.47754V4.51367H13.2186C13.6328 4.51367 13.9686 4.84946 13.9686 5.26367C13.9686 5.67789 13.6328 6.01367 13.2186 6.01367H11.9999V7.94324H13.2186C13.6328 7.94324 13.9686 8.27902 13.9686 8.69324C13.9686 9.10745 13.6328 9.44324 13.2186 9.44324H11.9999V10.4775C11.9999 11.306 11.3283 11.9775 10.4999 11.9775H9.46468V13.2099C9.46468 13.6241 9.1289 13.9599 8.71468 13.9599C8.30047 13.9599 7.96468 13.6241 7.96468 13.2099V11.9775H6.03524V13.2099C6.03524 13.6241 5.69945 13.9599 5.28524 13.9599C4.87103 13.9599 4.53524 13.6241 4.53524 13.2099V11.9775H3.49984C2.67141 11.9775 1.99984 11.306 1.99984 10.4775V9.44324H0.783203C0.368989 9.44324 0.0332031 9.10745 0.0332031 8.69324C0.0332031 8.27902 0.368989 7.94324 0.783203 7.94324H1.99984V6.01367H0.783203C0.368989 6.01367 0.0332031 5.67789 0.0332031 5.26367C0.0332031 4.84946 0.368989 4.51367 0.783203 4.51367H1.99984V3.47754C1.99984 2.64911 2.67141 1.97754 3.49984 1.97754H4.53524V0.790039C4.53524 0.375825 4.87103 0.0400391 5.28524 0.0400391C5.69945 0.0400391 6.03524 0.375825 6.03524 0.790039V1.97754H7.96468V0.790039C7.96468 0.375825 8.30047 0.0400391 8.71468 0.0400391ZM6.24357 4.5682C6.41951 3.76446 7.56392 3.75946 7.74688 4.56163L7.75499 4.59732C7.76038 4.62113 7.76543 4.64342 7.7708 4.66628C7.96264 5.48335 8.62389 6.10768 9.45164 6.25169C10.2908 6.39768 10.2908 7.60232 9.45164 7.74831C8.61948 7.89308 7.95559 8.52332 7.76776 9.34682L7.74688 9.43837C7.56392 10.2405 6.41951 10.2355 6.24357 9.4318L6.22638 9.35325C6.04547 8.52684 5.38257 7.89201 4.54911 7.74701C3.71142 7.60128 3.71141 6.39872 4.54911 6.25299C5.37967 6.10849 6.04086 5.47757 6.22447 4.65539L6.23707 4.598L6.24357 4.5682Z\" fill=\"black\"/>\n</g>\n<defs>\n<clipPath id=\"clip0_1068_189544\">\n<rect width=\"14\" height=\"14\" fill=\"white\"/>\n</clipPath>\n</defs>\n</svg>\n" }, { "name": "ai-cloud-spark", "keywords": [ "cloud", "internet", "server", "network", "artificial", "intelligence", "ai" ], "content": "<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <g clip-path=\"url(#clip0_1068_189529)\">\n <path fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n d=\"M7.88381 0.165291C6.89196 0.0450664 5.88886 0.268505 5.04181 0.798345C4.25349 1.29145 3.64467 2.02219 3.30135 2.88247C2.87262 2.92977 2.45508 3.05187 2.06781 3.24362C1.63324 3.45878 1.24531 3.75744 0.92616 4.12253C0.281611 4.85986 -0.0436335 5.82304 0.0219743 6.80017C0.087582 7.77731 0.538668 8.68836 1.276 9.33291C1.52925 9.55429 1.80913 9.738 2.10708 9.88114C2.23853 9.09496 2.79956 8.38069 3.79016 8.20836C4.35848 8.10949 4.81104 7.67811 4.93736 7.11585L4.96023 7.01146C5.43591 4.83845 8.53 4.82492 9.02466 6.9937L9.03475 7.03814C9.04166 7.06865 9.047 7.09221 9.05244 7.11538C9.18449 7.6778 9.63973 8.10763 10.2096 8.20676C11.2609 8.38966 11.8287 9.18251 11.9131 10.0243C12.2792 9.884 12.6197 9.67707 12.9162 9.41068C13.5042 8.88231 13.878 8.15668 13.9669 7.37119C14.0558 6.58569 13.8536 5.79486 13.3985 5.14845C12.9811 4.55545 12.3775 4.12175 11.6866 3.91417C11.5613 2.99088 11.1436 2.12994 10.4923 1.45901C9.79633 0.742138 8.87567 0.285516 7.88381 0.165291ZM6.18132 7.27876C6.37146 6.41016 7.60823 6.40476 7.80595 7.27166L7.81577 7.3149C7.8225 7.34462 7.82879 7.37239 7.83553 7.4011C8.08204 8.451 8.93171 9.25323 9.99531 9.43826C10.9004 9.59573 10.9004 10.8951 9.99531 11.0525C8.92604 11.2386 8.07299 12.0484 7.83164 13.1065L7.80595 13.2191C7.60823 14.086 6.37146 14.0806 6.18132 13.212L6.16017 13.1154C5.92765 12.0532 5.07564 11.2373 4.0044 11.0509C3.10108 10.8938 3.10107 9.59701 4.0044 9.43986C5.07191 9.25414 5.92172 8.44323 6.15771 7.38648L6.17343 7.31494L6.18132 7.27876Z\"\n fill=\"black\"/>\n </g>\n <defs>\n <clipPath id=\"clip0_1068_189529\">\n <rect width=\"14\" height=\"14\" fill=\"white\"/>\n </clipPath>\n </defs>\n</svg>\n" }, { "name": "ai-edit-spark", "keywords": [ "change", "edit", "modify", "pencil", "write", "writing", "artificial", "intelligence", "ai" ], "content": "<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<g clip-path=\"url(#clip0_1068_189535)\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M2.85637 0.654372C3.04651 -0.214226 4.28328 -0.219632 4.481 0.647271L4.49082 0.690505C4.49756 0.720231 4.50384 0.747995 4.51058 0.776709C4.75709 1.82661 5.60676 2.62884 6.67036 2.81387C7.57548 2.97134 7.57548 4.27068 6.67036 4.42815C5.60109 4.61417 4.74804 5.42398 4.50669 6.48214L4.481 6.59475C4.28328 7.46166 3.04651Line truncated
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 765 KiB

@@ -1,14 +0,0 @@
"{""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,12 +0,0 @@
# dart_dependency_validator.yaml
allow_pins: true
include:
- "lib/**"
exclude:
- "packages/**"
ignore:
- analyzer
@@ -0,0 +1,71 @@
// ignore_for_file: unused_import
import 'dart:io';
import 'dart:ui';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:path/path.dart' as p;
import '../shared/dir.dart';
import '../shared/mock/mock_file_picker.dart';
import '../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('appflowy cloud', () {
testWidgets('anon user and then sign in', (tester) async {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloudSelfHost,
);
tester.expectToSeeText(LocaleKeys.signIn_loginStartWithAnonymous.tr());
await tester.tapAnonymousSignInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
// reanme the name of the anon user
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
final userNameFinder = find.descendant(
of: find.byType(SettingsUserView),
matching: find.byType(UserNameInput),
);
await tester.enterText(userNameFinder, 'local_user');
await tester.openSettingsPage(SettingsPage.user);
await tester.pumpAndSettle();
// sign up with Google
await tester.tapGoogleLoginInButton();
// sign out
await tester.expectToSeeHomePage();
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
await tester.logout();
await tester.pumpAndSettle();
// tap the continue as anonymous button
await tester
.tapButton(find.text(LocaleKeys.signIn_loginStartWithAnonymous.tr()));
await tester.expectToSeeHomePage();
// New anon user name
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.user);
final userNameInput = tester.widget(userNameFinder) as UserNameInput;
expect(userNameInput.name, 'Me');
});
});
}
@@ -6,17 +6,16 @@ import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/pages/account/account.dart';
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
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();
@@ -38,21 +37,14 @@ void main() {
// Open the setting page and sign out
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);
// Scroll to sign-out
await tester.scrollUntilVisible(
find.byType(AccountSignInOutButton),
100,
scrollable: find.findSettingsScrollable(),
);
await tester.tapButton(find.byType(AccountSignInOutButton));
await tester.openSettingsPage(SettingsPage.user);
await tester.tapButton(find.byType(SettingLogoutButton));
tester.expectToSeeText(LocaleKeys.button_ok.tr());
await tester.tapButtonWithName(LocaleKeys.button_ok.tr());
// Go to the sign in page again
await tester.pumpAndSettle(const Duration(seconds: 5));
await tester.pumpAndSettle(const Duration(seconds: 1));
tester.expectToSeeGoogleLoginButton();
});
@@ -64,16 +56,7 @@ void main() {
// should not see the sync setting page when sign in as anonymous
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);
// Scroll to sign-in
await tester.scrollUntilVisible(
find.byType(AccountSignInOutButton),
100,
scrollable: find.findSettingsScrollable(),
);
await tester.tapButton(find.byType(AccountSignInOutButton));
await tester.openSettingsPage(SettingsPage.user);
tester.expectToSeeGoogleLoginButton();
});
@@ -86,13 +69,10 @@ 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);
@@ -100,7 +80,6 @@ void main() {
// the switch should be on after toggling
await tester.toggleEnableSync(AppFlowyCloudEnableSync);
tester.assertAppFlowyCloudEnableSyncSwitchValue(true);
await tester.wait(250);
});
});
}
@@ -0,0 +1,25 @@
import 'anon_user_continue_test.dart' as anon_user_continue_test;
import 'appflowy_cloud_auth_test.dart' as appflowy_cloud_auth_test;
import 'empty_test.dart' as preset_af_cloud_env_test;
// import 'document_sync_test.dart' as document_sync_test;
import 'user_setting_sync_test.dart' as user_sync_test;
import 'workspace/change_name_and_icon_test.dart'
as change_workspace_name_and_icon_test;
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();
// document_sync_test.main();
user_sync_test.main();
anon_user_continue_test.main();
// workspace
collaboration_workspace_test.main();
change_workspace_name_and_icon_test.main();
}
@@ -8,18 +8,17 @@ import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package: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 'package:integration_test/integration_test.dart';
import '../shared/dir.dart';
import '../shared/mock/mock_file_picker.dart';
import '../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@@ -46,11 +45,12 @@ void main() {
await tester.ime.insertText(inputContent);
expect(find.text(inputContent, findRichText: true), findsOneWidget);
// TODO(nathan): remove the await
// 6 seconds for data sync
await tester.waitForSeconds(6);
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);
await tester.openSettingsPage(SettingsPage.user);
await tester.logout();
});
@@ -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() {
@@ -0,0 +1,89 @@
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_supabase_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../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.user);
await tester.tapButton(find.byType(SettingLogoutButton));
tester.expectToSeeText(LocaleKeys.button_ok.tr());
await tester.tapButtonWithName(LocaleKeys.button_ok.tr());
// Go to the sign in page again
await tester.pumpAndSettle(const Duration(seconds: 1));
tester.expectToSeeGoogleLoginButton();
});
testWidgets('sign in as 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.user);
tester.expectToSeeGoogleLoginButton();
});
// testWidgets('enable encryption', (tester) async {
// await tester.initializeAppFlowy(cloudType: CloudType.supabase);
// await tester.tapGoogleLoginInButton();
// // Open the setting page and sign out
// await tester.openSettings();
// await tester.openSettingsPage(SettingsPage.cloud);
// // the switch should be off by default
// tester.assertEnableEncryptSwitchValue(false);
// await tester.toggleEnableEncrypt();
// // the switch should be on after toggling
// tester.assertEnableEncryptSwitchValue(true);
// // the switch can not be toggled back to off
// await tester.toggleEnableEncrypt();
// tester.assertEnableEncryptSwitchValue(true);
// });
testWidgets('enable sync', (tester) async {
await tester.initializeAppFlowy(cloudType: AuthenticatorType.supabase);
await tester.tapGoogleLoginInButton();
// Open the setting page and sign out
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.cloud);
// the switch should be on by default
tester.assertSupabaseEnableSyncSwitchValue(true);
await tester.toggleEnableSync(SupabaseEnableSync);
// the switch should be off
tester.assertSupabaseEnableSyncSwitchValue(false);
// the switch should be on after toggling
await tester.toggleEnableSync(SupabaseEnableSync);
tester.assertSupabaseEnableSyncSwitchValue(true);
});
});
}
@@ -3,31 +3,26 @@
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/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/pages/account/account_user_profile.dart';
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flowy_infra_ui/style_widget/text_field.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';
import '../../board/board_hide_groups_test.dart';
import 'package:integration_test/integration_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';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
@@ -44,15 +39,35 @@ void main() {
await tester.expectToSeeHomePageWithGetStartedPage();
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);
await tester.openSettingsPage(SettingsPage.user);
// final userAvatarFinder = find.descendant(
// of: find.byType(SettingsUserView),
// matching: find.byType(UserAvatar),
// );
await tester.enterUserName(name);
await tester.pumpAndSettle(const Duration(seconds: 6));
await tester.logout();
// Open icon picker dialog and select emoji
// await tester.tap(userAvatarFinder);
// await tester.pumpAndSettle();
// await tester.tapEmoji('😁');
// await tester.pumpAndSettle();
// final UserAvatar userAvatar =
// tester.widget(userAvatarFinder) as UserAvatar;
// expect(userAvatar.iconUrl, '😁');
// enter user name
final userNameFinder = find.descendant(
of: find.byType(SettingsUserView),
matching: find.byType(UserNameInput),
);
await tester.enterText(userNameFinder, name);
await tester.pumpAndSettle();
await tester.tapEscButton();
// wait 2 seconds for the sync to finish
await tester.pumpAndSettle(const Duration(seconds: 2));
});
});
testWidgets('get user icon and name from server', (tester) async {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloudSelfHost,
@@ -63,12 +78,23 @@ void main() {
await tester.pumpAndSettle();
await tester.openSettings();
await tester.openSettingsPage(SettingsPage.account);
await tester.openSettingsPage(SettingsPage.user);
// Verify name
final profileSetting =
tester.widget(find.byType(AccountUserProfile)) as AccountUserProfile;
// verify icon
// final userAvatarFinder = find.descendant(
// of: find.byType(SettingsUserView),
// matching: find.byType(UserAvatar),
// );
// final UserAvatar userAvatar = tester.widget(userAvatarFinder) as UserAvatar;
// expect(userAvatar.iconUrl, '😁');
expect(profileSetting.name, name);
// verify name
final userNameFinder = find.descendant(
of: find.byType(SettingsUserView),
matching: find.byType(UserNameInput),
);
final UserNameInput userNameInput =
tester.widget(userNameFinder) as UserNameInput;
expect(userNameInput.name, name);
});
}
@@ -9,15 +9,16 @@ import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flutter_test/flutter_test.dart';
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();
@@ -10,10 +10,11 @@ import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar_workspace.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_actions.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/sidebar_workspace.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
@@ -23,16 +24,18 @@ 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';
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: ', () {
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 {
@@ -43,6 +46,7 @@ void main() {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloudSelfHost,
email: email,
);
await tester.tapGoogleLoginInButton();
await tester.expectToSeeHomePageWithGetStartedPage();
@@ -56,12 +60,9 @@ void main() {
Finder success;
final Finder items = find.byType(WorkspaceMenuItem);
// delete the newly created workspace
await tester.openCollaborativeWorkspaceMenu();
await tester.pumpUntilFound(items);
final Finder items = find.byType(WorkspaceMenuItem);
expect(items, findsNWidgets(2));
expect(
tester.widget<WorkspaceMenuItem>(items.last).workspace.name,
@@ -1,7 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/plugins/database/board/presentation/board_page.dart';
import 'package:appflowy/plugins/database/board/presentation/widgets/board_column_header.dart';
import 'package:appflowy/plugins/database/widgets/card/card.dart';
import 'package:appflowy/plugins/database/widgets/card/container/card_container.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_board/appflowy_board.dart';
import 'package:flutter/material.dart';
@@ -23,16 +22,14 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final firstCard = find.byType(RowCard).first;
expect(
find.descendant(
of: firstCard,
matching: find.text(defaultFirstCardName),
),
findsOneWidget,
final findFirstCard = find.descendant(
of: find.byType(AppFlowyGroupCard),
matching: find.byType(Text),
);
Text firstCardText = tester.firstWidget(findFirstCard);
expect(firstCardText.data, defaultFirstCardName);
await tester.tap(
find
.descendant(
@@ -48,7 +45,7 @@ void main() {
const newCardName = 'Card 4';
await tester.enterText(
find.descendant(
of: firstCard,
of: find.byType(RowCardContainer),
matching: find.byType(TextField),
),
newCardName,
@@ -58,13 +55,8 @@ void main() {
await tester.tap(find.byType(AppFlowyBoard));
await tester.pumpAndSettle();
expect(
find.descendant(
of: find.byType(RowCard).first,
matching: find.text(newCardName),
),
findsOneWidget,
);
firstCardText = tester.firstWidget(findFirstCard);
expect(firstCardText.data, newCardName);
});
testWidgets('from footer', (tester) async {
@@ -73,41 +65,39 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final lastCard = find.byType(RowCard).last;
expect(
find.descendant(
of: lastCard,
matching: find.text(defaultLastCardName),
),
findsOneWidget,
final findLastCard = find.descendant(
of: find.byType(AppFlowyGroupCard),
matching: find.byType(Text),
);
await tester.tapButton(
find.byType(BoardColumnFooter).at(1),
Text? lastCardText = tester.widgetList(findLastCard).last as Text;
expect(lastCardText.data, defaultLastCardName);
await tester.tap(
find
.descendant(
of: find.byType(AppFlowyGroupFooter),
matching: find.byType(FlowySvg),
)
.at(1),
);
await tester.pumpAndSettle();
const newCardName = 'Card 4';
await tester.enterText(
find.descendant(
of: find.byType(BoardColumnFooter),
of: find.byType(RowCardContainer),
matching: find.byType(TextField),
),
newCardName,
);
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle(const Duration(milliseconds: 500));
await tester.tap(find.byType(AppFlowyBoard));
await tester.pumpAndSettle();
expect(
find.descendant(
of: find.byType(RowCard).last,
matching: find.text(newCardName),
),
findsOneWidget,
);
lastCardText = tester.widgetList(findLastCard).last as Text;
expect(lastCardText.data, newCardName);
});
});
}
@@ -1,38 +0,0 @@
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../../shared/database_test_op.dart';
import '../../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('board field test', () {
testWidgets('change field type whithin card #5360', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
const name = 'Card 1';
final card1 = find.text(name);
await tester.tapButton(card1);
const fieldName = "test change field";
await tester.createField(
FieldType.RichText,
name: fieldName,
layout: ViewLayoutPB.Board,
);
await tester.dismissRowDetailPage();
await tester.tapButton(card1);
await tester.changeFieldTypeOfFieldWithName(
fieldName,
FieldType.Checkbox,
layout: ViewLayoutPB.Board,
);
await tester.hoverOnWidget(find.text('Card 2'));
});
});
}
@@ -1,21 +1,17 @@
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/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:appflowy_board/appflowy_board.dart';
import '../../shared/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();
@@ -24,7 +20,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Board);
final card1 = find.ancestor(
of: find.text(card1Name),
matching: find.byType(RowCard),
matching: find.byType(AppFlowyGroupCard),
);
final doingGroup = find.text('Doing');
final doingGroupCenter = tester.getCenter(doingGroup);
@@ -50,105 +46,5 @@ 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,
);
});
});
}
@@ -1,14 +1,13 @@
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() {
@@ -78,47 +77,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.tapButtonWithName(LocaleKeys.space_delete.tr());
// Tap the delete button and confirm
await tester.tapButton(find.byFlowySvg(FlowySvgs.delete_s));
await tester.tapDialogOkButton();
// Expect number of groups to decrease by one
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 3);
});
// Expect number of groups to decrease by one
expect(tester.widgetList(find.byType(BoardColumnHeader)).length, 3);
});
}
@@ -29,7 +29,6 @@ void main() {
},
);
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
await tester.tapButtonWithName(LocaleKeys.button_delete.tr());
expect(find.text(name), findsNothing);
});
@@ -51,37 +50,6 @@ 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();
@@ -1,18 +1,14 @@
import 'package:integration_test/integration_test.dart';
import 'board_row_test.dart' as board_row_test;
import 'board_add_row_test.dart' as board_add_row_test;
import 'board_group_test.dart' as board_group_test;
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() {
void startTesting() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
// Board integration tests
board_row_test.main();
board_add_row_test.main();
board_group_test.main();
board_field_test.main();
board_hide_groups_test.main();
}
@@ -1,22 +0,0 @@
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();
}
@@ -1,80 +0,0 @@
// ignore_for_file: unused_import
import 'dart:io';
import 'dart:ui';
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/af_cloud_mock_auth_service.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/settings/pages/account/account.dart';
import 'package:appflowy/workspace/presentation/settings/pages/settings_account_view.dart';
import 'package:appflowy/workspace/presentation/settings/shared/settings_body.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/uuid.dart';
import 'package:flowy_infra_ui/style_widget/text_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:intl/intl.dart';
import 'package:path/path.dart' as p;
import '../../../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 -> sign in -> open imported space', (tester) async {
await tester.initializeAppFlowy(
cloudType: AuthenticatorType.appflowyCloudSelfHost,
);
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);
await tester.pumpAndSettle();
await tester.enterUserName('local_user');
// Scroll to sign-in
await tester.scrollUntilVisible(
find.byType(AccountSignInOutButton),
100,
scrollable: find.findSettingsScrollable(),
);
await tester.tapButton(find.byType(AccountSignInOutButton));
// sign up with Google
await tester.tapGoogleLoginInButton();
// await tester.pumpAndSettle(const Duration(seconds: 16));
// open the imported space
await tester.expectToSeeHomePage();
await tester.clickSpaceHeader();
// 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.pumpAndSettle();
});
});
}
@@ -1,5 +0,0 @@
import 'anon_user_data_migration_test.dart' as anon_user_test;
void main() async {
anon_user_test.main();
}
@@ -1,146 +0,0 @@
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)));
});
});
}
@@ -1,9 +0,0 @@
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();
}
@@ -1,121 +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 '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);
});
});
}
@@ -1,9 +0,0 @@
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();
}
@@ -1,76 +0,0 @@
// 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);
});
});
}
@@ -1,96 +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/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,
);
},
);
});
}
@@ -1,15 +0,0 @@
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();
}
@@ -1,4 +1,3 @@
import 'package:appflowy/util/field_type_extension.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -42,7 +41,7 @@ void main() {
name: 'my grid',
layout: ViewLayoutPB.Grid,
);
await tester.createField(FieldType.RichText, name: 'description');
await tester.createField(FieldType.RichText, 'description');
await tester.editCell(
rowIndex: 0,
@@ -82,7 +81,7 @@ void main() {
const fieldType = FieldType.Number;
// Create a number field
await tester.createField(fieldType);
await tester.createField(fieldType, fieldType.name);
await tester.editCell(
rowIndex: 0,
@@ -158,7 +157,7 @@ void main() {
const fieldType = FieldType.CreatedTime;
// Create a create time field
// The create time field is not editable
await tester.createField(fieldType);
await tester.createField(fieldType, fieldType.name);
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
@@ -176,7 +175,7 @@ void main() {
const fieldType = FieldType.LastEditedTime;
// Create a last time field
// The last time field is not editable
await tester.createField(fieldType);
await tester.createField(fieldType, fieldType.name);
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
@@ -192,7 +191,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.DateTime;
await tester.createField(fieldType);
await tester.createField(fieldType, fieldType.name);
// Tap the cell to invoke the field editor
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
@@ -211,21 +210,21 @@ void main() {
await tester.toggleIncludeTime();
// Select a date
final now = DateTime.now();
await tester.selectDay(content: now.day);
final today = DateTime.now();
await tester.selectDay(content: today.day);
await tester.dismissCellEditor();
tester.assertCellContent(
rowIndex: 0,
fieldType: FieldType.DateTime,
content: DateFormat('MMM dd, y').format(now),
content: DateFormat('MMM dd, y').format(today),
);
await tester.tapCellInGrid(rowIndex: 0, fieldType: fieldType);
// Toggle include time
// When toggling include time, the time value is from the previous existing date time, not the current time
final now = DateTime.now();
await tester.toggleIncludeTime();
await tester.dismissCellEditor();
@@ -299,7 +298,7 @@ void main() {
await tester.dismissCellEditor();
// Make sure the option is created and displayed in the cell
tester.findSelectOptionWithNameInGrid(
await tester.findSelectOptionWithNameInGrid(
rowIndex: 0,
name: 'tag 1',
);
@@ -311,12 +310,12 @@ void main() {
await tester.createOption(name: 'tag 2');
await tester.dismissCellEditor();
tester.findSelectOptionWithNameInGrid(
await tester.findSelectOptionWithNameInGrid(
rowIndex: 0,
name: 'tag 2',
);
tester.assertNumberOfSelectedOptionsInGrid(
await tester.assertNumberOfSelectedOptionsInGrid(
rowIndex: 0,
matcher: findsOneWidget,
);
@@ -328,12 +327,12 @@ void main() {
await tester.selectOption(name: 'tag 1');
await tester.dismissCellEditor();
tester.findSelectOptionWithNameInGrid(
await tester.findSelectOptionWithNameInGrid(
rowIndex: 0,
name: 'tag 1',
);
tester.assertNumberOfSelectedOptionsInGrid(
await tester.assertNumberOfSelectedOptionsInGrid(
rowIndex: 0,
matcher: findsOneWidget,
);
@@ -345,7 +344,7 @@ void main() {
await tester.selectOption(name: 'tag 1');
await tester.dismissCellEditor();
tester.assertNumberOfSelectedOptionsInGrid(
await tester.assertNumberOfSelectedOptionsInGrid(
rowIndex: 0,
matcher: findsNothing,
);
@@ -367,7 +366,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.MultiSelect;
await tester.createField(fieldType, name: fieldType.i18n);
await tester.createField(fieldType, fieldType.name);
// Tap the cell to invoke the selection option editor
await tester.tapSelectOptionCellInGrid(rowIndex: 0, fieldType: fieldType);
@@ -378,7 +377,7 @@ void main() {
await tester.dismissCellEditor();
// Make sure the option is created and displayed in the cell
tester.findSelectOptionWithNameInGrid(
await tester.findSelectOptionWithNameInGrid(
rowIndex: 0,
name: tags.first,
);
@@ -393,13 +392,13 @@ void main() {
await tester.dismissCellEditor();
for (final tag in tags) {
tester.findSelectOptionWithNameInGrid(
await tester.findSelectOptionWithNameInGrid(
rowIndex: 0,
name: tag,
);
}
tester.assertNumberOfSelectedOptionsInGrid(
await tester.assertNumberOfSelectedOptionsInGrid(
rowIndex: 0,
matcher: findsNWidgets(4),
);
@@ -413,7 +412,7 @@ void main() {
}
await tester.dismissCellEditor();
tester.assertNumberOfSelectedOptionsInGrid(
await tester.assertNumberOfSelectedOptionsInGrid(
rowIndex: 0,
matcher: findsNothing,
);
@@ -426,16 +425,16 @@ void main() {
await tester.selectOption(name: tags[3]);
await tester.dismissCellEditor();
tester.findSelectOptionWithNameInGrid(
await tester.findSelectOptionWithNameInGrid(
rowIndex: 0,
name: tags[1],
);
tester.findSelectOptionWithNameInGrid(
await tester.findSelectOptionWithNameInGrid(
rowIndex: 0,
name: tags[3],
);
tester.assertNumberOfSelectedOptionsInGrid(
await tester.assertNumberOfSelectedOptionsInGrid(
rowIndex: 0,
matcher: findsNWidgets(2),
);
@@ -450,7 +449,7 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
const fieldType = FieldType.Checklist;
await tester.createField(fieldType);
await tester.createField(fieldType, fieldType.name);
// assert that there is no progress bar in the grid
tester.assertChecklistCellInGrid(rowIndex: 0, percent: null);
@@ -462,22 +461,22 @@ void main() {
tester.assertChecklistEditorVisible(visible: true);
// create a new task with enter
await tester.createNewChecklistTask(name: "task 1", enter: true);
await tester.createNewChecklistTask(name: "task 0", enter: true);
// assert that the task is displayed
tester.assertChecklistTaskInEditor(
index: 0,
name: "task 1",
name: "task 0",
isChecked: false,
);
// update the task's name
await tester.renameChecklistTask(index: 0, name: "task 11");
await tester.renameChecklistTask(index: 0, name: "task 1");
// assert that the task's name is updated
tester.assertChecklistTaskInEditor(
index: 0,
name: "task 11",
name: "task 1",
isChecked: false,
);
@@ -1,5 +1,5 @@
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/setting_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pbenum.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
@@ -10,7 +10,7 @@ import '../../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('grid field settings test:', () {
group('database field settings', () {
testWidgets('field visibility', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
@@ -50,15 +50,6 @@ 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");
});
});
}
@@ -1,13 +1,12 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/database/widgets/field/type_option_editor/select/select_option.dart';
import 'package:appflowy/util/field_type_extension.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package: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 'package:intl/intl.dart';
import '../../shared/database_test_op.dart';
import '../../shared/util.dart';
@@ -15,7 +14,7 @@ import '../../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('grid edit field test:', () {
group('grid field editor:', () {
testWidgets('rename existing field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
@@ -24,6 +23,7 @@ void main() {
// Invoke the field editor
await tester.tapGridFieldWithName('Name');
await tester.tapEditFieldButton();
await tester.renameField('hello world');
await tester.dismissFieldEditor();
@@ -32,32 +32,6 @@ 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();
@@ -82,22 +56,11 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field
await tester.createField(FieldType.Checklist);
tester.findFieldWithName(FieldType.Checklist.i18n);
await tester.createField(FieldType.Checklist, 'checklist');
// editing field type during field creation should change title
await tester.createField(FieldType.MultiSelect);
tester.findFieldWithName(FieldType.MultiSelect.i18n);
// not if the user changes the title manually though
const name = "New field";
await tester.createField(FieldType.DateTime);
await tester.tapGridFieldWithName(FieldType.DateTime.i18n);
await tester.renameField(name);
await tester.tapEditFieldButton();
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.URL);
tester.findFieldWithName(name);
// check the field is created successfully
tester.findFieldWithName('checklist');
await tester.pumpAndSettle();
});
testWidgets('delete field', (tester) async {
@@ -107,14 +70,14 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field
await tester.createField(FieldType.Checkbox, name: 'New field 1');
await tester.createField(FieldType.Checkbox, 'New field 1');
// Delete the field
await tester.tapGridFieldWithName('New field 1');
await tester.tapDeletePropertyButton();
// confirm delete
await tester.tapButtonWithName(LocaleKeys.space_delete.tr());
await tester.tapDialogOkButton();
tester.noFieldWithName('New field 1');
await tester.pumpAndSettle();
@@ -127,7 +90,10 @@ void main() {
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
// create a field
await tester.createField(FieldType.RichText, name: 'New field 1');
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
await tester.renameField('New field 1');
await tester.dismissFieldEditor();
// duplicate the field
await tester.tapGridFieldWithName('New field 1');
@@ -151,7 +117,7 @@ void main() {
await tester.dismissFieldEditor();
tester.findFieldWithName('Right');
// insert new field to the left
// insert new field to the right
await tester.tapGridFieldWithName('Type');
await tester.tapInsertFieldButton(left: true, name: "Left");
await tester.dismissFieldEditor();
@@ -160,6 +126,26 @@ void main() {
await tester.pumpAndSettle();
});
testWidgets('create checklist field', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
await tester.createNewPageWithNameUnderParent(layout: ViewLayoutPB.Grid);
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
// Open the type option menu
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.Checklist);
// After update the field type, the cells should be updated
await tester.findCellByFieldType(FieldType.Checklist);
await tester.pumpAndSettle();
});
testWidgets('create list of fields', (tester) async {
await tester.initializeAppFlowy();
await tester.tapAnonymousSignInButton();
@@ -176,10 +162,18 @@ void main() {
FieldType.CreatedTime,
FieldType.Checkbox,
]) {
await tester.createField(fieldType);
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
await tester.renameField(fieldType.name);
// Open the type option menu
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(fieldType);
await tester.dismissFieldEditor();
// After update the field type, the cells should be updated
tester.findCellByFieldType(fieldType);
await tester.findCellByFieldType(fieldType);
await tester.pumpAndSettle();
}
});
@@ -196,7 +190,15 @@ void main() {
FieldType.Checklist,
FieldType.URL,
]) {
await tester.createField(fieldType);
// create the field
await tester.scrollToRight(find.byType(GridPage));
await tester.tapNewPropertyButton();
await tester.renameField(fieldType.i18n);
// change field type
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(fieldType);
await tester.dismissFieldEditor();
// open the field editor
await tester.tapGridFieldWithName(fieldType.i18n);
@@ -216,7 +218,11 @@ void main() {
await tester.scrollToRight(find.byType(GridPage));
// create a number field
await tester.createField(FieldType.Number);
await tester.tapNewPropertyButton();
await tester.renameField("Number");
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.Number);
await tester.dismissFieldEditor();
// enter some data into the first number cell
await tester.editCell(
@@ -237,7 +243,7 @@ void main() {
);
// open editor and change number format
await tester.tapGridFieldWithName(FieldType.Number.i18n);
await tester.tapGridFieldWithName('Number');
await tester.tapEditFieldButton();
await tester.changeNumberFieldFormat();
await tester.dismissFieldEditor();
@@ -270,6 +276,7 @@ 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));
@@ -285,7 +292,11 @@ void main() {
await tester.scrollToRight(find.byType(GridPage));
// create a date field
await tester.createField(FieldType.DateTime);
await tester.tapNewPropertyButton();
await tester.renameField(FieldType.DateTime.i18n);
await tester.tapSwitchFieldTypeButton();
await tester.selectFieldType(FieldType.DateTime);
await tester.dismissFieldEditor();
// edit the first date cell
await tester.tapCellInGrid(rowIndex: 0, fieldType: FieldType.DateTime);
@@ -381,189 +392,5 @@ 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",
);
});
});
}
@@ -1,21 +1,17 @@
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/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pbenum.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import '../../shared/database_test_op.dart';
import '../../shared/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('grid filter:', () {
testWidgets('add text filter', (tester) async {
await tester.openTestDatabase(v020GridFileName);
await tester.openV020database();
// create a filter
await tester.tapDatabaseFilterButton();
@@ -23,68 +19,68 @@ void main() {
await tester.tapFilterButtonInGrid('Name');
// enter 'A' in the filter text field
tester.assertNumberOfRowsInGridPage(10);
await tester.assertNumberOfRowsInGridPage(10);
await tester.enterTextInTextFilter('A');
tester.assertNumberOfRowsInGridPage(1);
await tester.assertNumberOfRowsInGridPage(1);
// after remove the filter, the grid should show all rows
await tester.enterTextInTextFilter('');
tester.assertNumberOfRowsInGridPage(10);
await tester.assertNumberOfRowsInGridPage(10);
await tester.enterTextInTextFilter('B');
tester.assertNumberOfRowsInGridPage(1);
await tester.assertNumberOfRowsInGridPage(1);
// open the menu to delete the filter
await tester.tapDisclosureButtonInFinder(find.byType(TextFilterEditor));
await tester.tapDeleteFilterButtonInGrid();
tester.assertNumberOfRowsInGridPage(10);
await tester.assertNumberOfRowsInGridPage(10);
await tester.pumpAndSettle();
});
testWidgets('add checkbox filter', (tester) async {
await tester.openTestDatabase(v020GridFileName);
await tester.openV020database();
// create a filter
await tester.tapDatabaseFilterButton();
await tester.tapCreateFilterByFieldType(FieldType.Checkbox, 'Done');
tester.assertNumberOfRowsInGridPage(5);
await tester.assertNumberOfRowsInGridPage(5);
await tester.tapFilterButtonInGrid('Done');
await tester.tapCheckboxFilterButtonInGrid();
await tester.tapUnCheckedButtonOnCheckboxFilter();
tester.assertNumberOfRowsInGridPage(5);
await tester.assertNumberOfRowsInGridPage(5);
await tester
.tapDisclosureButtonInFinder(find.byType(CheckboxFilterEditor));
await tester.tapDeleteFilterButtonInGrid();
tester.assertNumberOfRowsInGridPage(10);
await tester.assertNumberOfRowsInGridPage(10);
await tester.pumpAndSettle();
});
testWidgets('add checklist filter', (tester) async {
await tester.openTestDatabase(v020GridFileName);
await tester.openV020database();
// create a filter
await tester.tapDatabaseFilterButton();
await tester.tapCreateFilterByFieldType(FieldType.Checklist, 'checklist');
// By default, the condition of checklist filter is 'uncompleted'
tester.assertNumberOfRowsInGridPage(9);
await tester.assertNumberOfRowsInGridPage(9);
await tester.tapFilterButtonInGrid('checklist');
await tester.tapChecklistFilterButtonInGrid();
await tester.tapCompletedButtonOnChecklistFilter();
tester.assertNumberOfRowsInGridPage(1);
await tester.assertNumberOfRowsInGridPage(1);
await tester.pumpAndSettle();
});
testWidgets('add single select filter', (tester) async {
await tester.openTestDatabase(v020GridFileName);
await tester.openV020database();
// create a filter
await tester.tapDatabaseFilterButton();
@@ -94,27 +90,27 @@ void main() {
// select the option 's6'
await tester.tapOptionFilterWithName('s6');
tester.assertNumberOfRowsInGridPage(0);
await tester.assertNumberOfRowsInGridPage(0);
// unselect the option 's6'
await tester.tapOptionFilterWithName('s6');
tester.assertNumberOfRowsInGridPage(10);
await tester.assertNumberOfRowsInGridPage(10);
// select the option 's5'
await tester.tapOptionFilterWithName('s5');
tester.assertNumberOfRowsInGridPage(1);
await tester.assertNumberOfRowsInGridPage(1);
// select the option 's4'
await tester.tapOptionFilterWithName('s4');
// The row with 's4' should be shown.
tester.assertNumberOfRowsInGridPage(1);
await tester.assertNumberOfRowsInGridPage(1);
await tester.pumpAndSettle();
});
testWidgets('add multi select filter', (tester) async {
await tester.openTestDatabase(v020GridFileName);
await tester.openV020database();
// create a filter
await tester.tapDatabaseFilterButton();
@@ -128,70 +124,17 @@ void main() {
// select the option 'm1'. Any option with 'm1' should be shown.
await tester.tapOptionFilterWithName('m1');
tester.assertNumberOfRowsInGridPage(5);
await tester.assertNumberOfRowsInGridPage(5);
await tester.tapOptionFilterWithName('m1');
// select the option 'm2'. Any option with 'm2' should be shown.
await tester.tapOptionFilterWithName('m2');
tester.assertNumberOfRowsInGridPage(4);
await tester.assertNumberOfRowsInGridPage(4);
await tester.tapOptionFilterWithName('m2');
// select the option 'm4'. Any option with 'm4' should be shown.
await tester.tapOptionFilterWithName('m4');
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.assertNumberOfRowsInGridPage(1);
await tester.pumpAndSettle();
});
@@ -1,312 +0,0 @@
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/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';
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';
import '../board/board_hide_groups_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('media type option in database', () {
testWidgets('add media field and add files two times', (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]);
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 one file
expect(find.byType(RenderMedia), findsOneWidget);
// Mock second file
mockPickFilePaths(paths: [secondImagePath]);
// 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('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()]);
});
});
}
Loaded 100 of 4040 files, more files were not shown because too many files have changed in this diff. Show more