test: 增加登录页面键盘弹出布局测试
This commit is contained in:
@@ -60,4 +60,28 @@ void main() {
|
|||||||
|
|
||||||
expect((topSpace - bottomSpace).abs(), lessThanOrEqualTo(2));
|
expect((topSpace - bottomSpace).abs(), lessThanOrEqualTo(2));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Login screen does not overflow when keyboard is visible', (
|
||||||
|
WidgetTester tester,
|
||||||
|
) async {
|
||||||
|
final mockAuthBloc = MockAuthBloc();
|
||||||
|
when(() => mockAuthBloc.state).thenReturn(AuthInitial());
|
||||||
|
when(
|
||||||
|
() => mockAuthBloc.stream,
|
||||||
|
).thenAnswer((_) => Stream.value(AuthInitial()));
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MediaQuery(
|
||||||
|
data: const MediaQueryData(
|
||||||
|
size: Size(390, 844),
|
||||||
|
viewInsets: EdgeInsets.only(bottom: 320),
|
||||||
|
),
|
||||||
|
child: LinksyApp(authBloc: mockAuthBloc),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(tester.takeException(), isNull);
|
||||||
|
expect(find.text('登录'), findsOneWidget);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+98
-20
@@ -7,14 +7,85 @@ COMPOSE_FILE="$ROOT_DIR/infra/docker/docker-compose.yml"
|
|||||||
ENV_FILE="$ROOT_DIR/.env"
|
ENV_FILE="$ROOT_DIR/.env"
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
echo "Usage: $0 {start|stop}"
|
echo "Usage: $0 {start|stop|restart}"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Commands:"
|
echo "Commands:"
|
||||||
echo " start Start web + worker processes in tmux"
|
echo " start Start web + worker processes in tmux"
|
||||||
echo " stop Stop and clean up tmux session"
|
echo " stop Stop tmux session and clean orphaned processes"
|
||||||
|
echo " restart Stop then start all app processes"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load_env_if_exists() {
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
. "$ENV_FILE"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
is_port_in_use() {
|
||||||
|
local port="$1"
|
||||||
|
|
||||||
|
if command -v lsof >/dev/null 2>&1; then
|
||||||
|
lsof -iTCP:"$port" -sTCP:LISTEN -t >/dev/null 2>&1
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v ss >/dev/null 2>&1; then
|
||||||
|
ss -ltn "sport = :$port" | awk 'NR > 1 {exit 0} END {exit 1}'
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_listening_pids() {
|
||||||
|
local port="$1"
|
||||||
|
|
||||||
|
if command -v lsof >/dev/null 2>&1; then
|
||||||
|
lsof -iTCP:"$port" -sTCP:LISTEN -t | sort -u
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v ss >/dev/null 2>&1; then
|
||||||
|
ss -lptn "sport = :$port" | awk -F 'pid=' 'NF > 1 {split($2, tmp, ","); print tmp[1]}' | sort -u
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
kill_pids_gracefully() {
|
||||||
|
local label="$1"
|
||||||
|
shift
|
||||||
|
local pids=("$@")
|
||||||
|
local alive=()
|
||||||
|
|
||||||
|
if [ "${#pids[@]}" -eq 0 ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Stopping ${label}: ${pids[*]}"
|
||||||
|
kill -TERM "${pids[@]}" 2>/dev/null || true
|
||||||
|
|
||||||
|
for _ in {1..10}; do
|
||||||
|
alive=()
|
||||||
|
for pid in "${pids[@]}"; do
|
||||||
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
alive+=("$pid")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#alive[@]}" -eq 0 ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Force killing ${label}: ${alive[*]}"
|
||||||
|
kill -KILL "${alive[@]}" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
echo "=== App Up ==="
|
echo "=== App Up ==="
|
||||||
echo "This script starts web + worker processes in tmux."
|
echo "This script starts web + worker processes in tmux."
|
||||||
@@ -36,10 +107,7 @@ start() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
set -a
|
load_env_if_exists
|
||||||
# shellcheck disable=SC1090
|
|
||||||
. "$ENV_FILE"
|
|
||||||
set +a
|
|
||||||
|
|
||||||
UVICORN_LOG_LEVEL="${SOCIAL_RUNTIME__LOG_LEVEL:-info}"
|
UVICORN_LOG_LEVEL="${SOCIAL_RUNTIME__LOG_LEVEL:-info}"
|
||||||
UVICORN_LOG_LEVEL="${UVICORN_LOG_LEVEL,,}"
|
UVICORN_LOG_LEVEL="${UVICORN_LOG_LEVEL,,}"
|
||||||
@@ -51,13 +119,11 @@ start() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if command -v ss >/dev/null 2>&1; then
|
if is_port_in_use "$WEB_PORT"; then
|
||||||
if ss -ltn | awk '{print $4}' | grep -qE "[:.]${WEB_PORT}$"; then
|
|
||||||
echo "Error: web port ${WEB_PORT} is already in use." >&2
|
echo "Error: web port ${WEB_PORT} is already in use." >&2
|
||||||
echo "Hint: run '$0 stop' or change SOCIAL_WEB__PORT in .env" >&2
|
echo "Hint: run '$0 stop' or change SOCIAL_WEB__PORT in .env" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Starting web + worker processes in tmux session '$SESSION_NAME'..."
|
echo "Starting web + worker processes in tmux session '$SESSION_NAME'..."
|
||||||
|
|
||||||
@@ -88,6 +154,8 @@ ${SOCIAL_WEB__WORKERS:-2} --log-level ${UVICORN_LOG_LEVEL}"
|
|||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
echo "=== App Down ==="
|
echo "=== App Down ==="
|
||||||
|
load_env_if_exists
|
||||||
|
WEB_PORT="${SOCIAL_WEB__PORT:-5775}"
|
||||||
|
|
||||||
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
|
||||||
echo "Stopping tmux session '$SESSION_NAME'..."
|
echo "Stopping tmux session '$SESSION_NAME'..."
|
||||||
@@ -97,24 +165,34 @@ stop() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Checking for orphaned processes..."
|
echo "Checking for orphaned processes..."
|
||||||
if pgrep -f "uvicorn.*app:app" > /dev/null 2>&1; then
|
|
||||||
echo "Killing orphaned uvicorn processes..."
|
mapfile -t uvicorn_pids < <(pgrep -f "uv run uvicorn app:app" || true)
|
||||||
pkill -f "uvicorn.*app:app"
|
kill_pids_gracefully "uvicorn" "${uvicorn_pids[@]}"
|
||||||
fi
|
|
||||||
if pgrep -f "gunicorn.*app:app" > /dev/null 2>&1; then
|
mapfile -t taskiq_pids < <(pgrep -f "uv run taskiq worker core.taskiq.app:" || true)
|
||||||
echo "Killing orphaned gunicorn processes..."
|
kill_pids_gracefully "taskiq workers" "${taskiq_pids[@]}"
|
||||||
pkill -f "gunicorn.*app:app"
|
|
||||||
fi
|
mapfile -t port_pids < <(collect_listening_pids "$WEB_PORT" || true)
|
||||||
if pgrep -f "taskiq.*worker" > /dev/null 2>&1; then
|
kill_pids_gracefully "port ${WEB_PORT} listeners" "${port_pids[@]}"
|
||||||
echo "Killing orphaned taskiq processes..."
|
|
||||||
pkill -f "taskiq.*worker"
|
if is_port_in_use "$WEB_PORT"; then
|
||||||
|
echo "Warning: port ${WEB_PORT} is still in use after cleanup." >&2
|
||||||
|
echo "Hint: check process with 'lsof -iTCP:${WEB_PORT} -sTCP:LISTEN'" >&2
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Session stopped and cleaned up."
|
echo "Session stopped and cleaned up."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
stop
|
||||||
|
echo ""
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
case "${1:-}" in
|
case "${1:-}" in
|
||||||
start) start ;;
|
start) start ;;
|
||||||
stop) stop ;;
|
stop) stop ;;
|
||||||
|
restart) restart ;;
|
||||||
*) usage ;;
|
*) usage ;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
Reference in New Issue
Block a user