Цель: набор тестов должен зарегистрировать нового пользователя, войти в него, а затем проверить, может ли он открыть страницу «создать аннотацию», которая извлекает его профиль пользователя из Firestore.
Проблема: тест, открывающий страницу «создание аннотации», всегда истекает. Он застревает в ожидании обновления пользовательского интерфейса данными из вызова getUserProfile. Это указывает на то, что вызову Firestore getDoc() на стороне клиента не удается получить документ.
Что я доказал: это очень тонкая проблема гонки или среды, потому что:
Ручное тестирование работает отлично. Если я выполню те же действия в реальном браузере, пользователь будет создан, и его профиль будет получен без проблем.
Функция облака работает успешно. Я проверил журналы функций Firebase, и функция onUserCreate успешно запускается каждый раз, когда тест регистрирует пользователя, и сообщает, что он успешно создал документ Firestore.
Документ существует (Admin SDK). Я добавил в блок beforeAll своего теста код, который использует Firebase Admin SDK для прямого подключения к базе данных. Он успешно находит документ пользователя еще до начала неудачного теста.
Аутентификация работает. Более простой тестовый пример из того же набора, который проверяет только состояние аутентификации, проходит успешно. Это доказывает, что тестовый браузер находится в состоянии входа в систему.
Это не проблема правил безопасности. Тест завершается неудачей, даже если мои правила Firestore для коллекции пользователей настроены как полностью общедоступные (разрешить чтение: если это правда;).
Основной вопрос: почему клиентский вызов Firestore getDoc() со страницы расширения, управляемого Puppeteer, не может получить документ, существование которого доказано, особенно если часть аутентификации SDK работает правильно в той же среде?
Мой Код:
- Тестовый сценарий (tests/extension.test.js): этот сценарий использует Admin SDK для ожидания документа, а затем запускает два теста. Первый (должен показывать состояние входа в систему...) проходит, но второй (должен разрешить вошедшему пользователю...) истекает время ожидания.
Код: Выделить всё
// tests/extension.test.js
import { describe, it, expect, jest } from '@jest/globals';
import { v4 as uuidv4 } from 'uuid';
import admin from 'firebase-admin';
import serviceAccount from '../service-account-key.json' with { type: 'json' };
admin.initializeApp({ /* ... credentials ... */ });
const db = admin.firestore();
async function waitForUserDocument(uid, timeout = 15000) {
const start = Date.now();
while (Date.now() - start < timeout) {
const userDoc = await db.collection('Users').doc(uid).get();
if (userDoc.exists) {
console.log(`Admin SDK found user document for ${uid}.`);
return userDoc.data();
}
await new Promise(resolve => setTimeout(resolve, 500));
}
throw new Error(`Admin SDK timed out waiting for user document for ${uid}.`);
}
describe('Anotato Extension Tests', () => {
jest.setTimeout(30000);
describe('User Actions', () => {
let extensionId;
let testUser = { /* ... email, password, uid ... */ };
beforeAll(async () => {
// ... Registers a new user via the UI and gets their UID ...
// ... This part works successfully ...
testUser.uid = await uidPromise;
// ... Then waits for the document using the Admin SDK helper ...
// This also works, the log "Admin SDK found user document" appears.
await waitForUserDocument(testUser.uid);
});
it('should show a signed-in state in the popup', async () => {
// This test programmatically signs in and checks the popup UI.
// IT PASSES.
const worker = await (await browser.waitForTarget(t => t.type() === 'service_worker')).worker();
await worker.evaluate(/* ... sign in logic ... */);
const popupPage = await browser.newPage();
await popupPage.goto(`chrome-extension://${extensionId}/html/popup.html`);
await popupPage.waitForFunction('document.body.innerText.includes("You are signed in")');
await popupPage.close();
});
it('should allow a signed-in user to create an annotation', async () => {
// This test also programmatically signs in.
const worker = await (await browser.waitForTarget(t => t.type() === 'service_worker')).worker();
await worker.evaluate(/* ... sign in logic ... */);
// It then opens the create annotation page.
const page = await browser.newPage();
await page.goto(`chrome-extension://${extensionId}/html/create_annotation.html?text=test&url=test`);
// IT FAILS HERE with a 30-second timeout.
await page.waitForFunction(
'document.querySelector("#credit-balance").textContent.includes("Create Credits:")'
);
// ... rest of test ...
});
});
});
- Логика выборки на стороне клиента (offscreen.js): страница create_annotation.html вызывает функцию getUserProfile, которая обрабатывается здесь. Похоже, что этот вызов завершается с ошибкой или истекает время ожидания.
Код: Выделить всё
// js/offscreen.js - using MODERN MODULAR Firebase SDK
import { getFirestore, doc, getDoc } from 'firebase/firestore';
import { getAuth, onAuthStateChanged } from 'firebase/auth';
// ... other imports
const auth = getAuth(app);
const db = getFirestore(app);
// ...
switch (message.type) {
case 'getUserProfile': {
const unsubscribe = onAuthStateChanged(auth, (user) => {
unsubscribe();
if (user) {
const userRef = doc(db, 'Users', user.uid);
// This getDoc() call seems to be the one that fails in the test.
getDoc(userRef)
.then(docSnap => {
if (docSnap.exists()) {
sendResponse({ success: true, profile: docSnap.data() });
} else {
sendResponse({ success: false, error: "User profile not found." });
}
})
.catch(err => sendResponse({ success: false, error: err.message }));
} else {
sendResponse({ success: false, error: "User not authenticated." });
}
});
return true;
}
}
// ...
- Функция Cloud (functions/index.js): мои ручные тесты и журналы Firebase доказали, что эта функция работает.
Код: Выделить всё
// functions/index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.initializeNewUser = functions.auth.user().onCreate(async (user) => {
const db = admin.firestore();
const settingsDoc = await db.collection("settings").doc("trustScoreRules").get();
const initialScore = settingsDoc.exists ? settingsDoc.data().initialScoreTom : 3;
const newUserProfile = {
email: user.email,
trustScore: initialScore,
createCreditsBalance: 5,
forwardCreditsBalance: 10,
// ... other fields
};
return db.collection("Users").doc(user.uid).set(newUserProfile);
});
@DoviShamir ➜ /workspaces/anotato-extension (main) $ npm test
anotato-extension@1.0.0 test
NODE_OPTIONS=--experimental-vm-modules jest
(node:16667) ExperimentalWarning: Модули VM — это экспериментальная функция, которая может измениться в любое время
(Используйте узел --trace-warnings ..., чтобы показать, где было создано предупреждение)
FAIL test/extension.test.js (6,175 с)
Тесты расширения Anotato
Код: Выделить всё
Extension Loading
✓ should load the extension and find the service worker (4 ms)
User Actions
✕ should show a signed-in state after signing in (292 ms)
✕ should allow a signed-in user to create an annotation (77 ms)
Код: Выделить всё
ReferenceError: _ref4 is not defined
at evaluate (evaluate at /workspaces/anotato-extension/tests/extension.test.js:98:32, :1:16)
at ExecutionContext.#evaluate (node_modules/puppeteer-core/src/cdp/ExecutionContext.ts:454:34)
at ExecutionContext.evaluate (node_modules/puppeteer-core/src/cdp/ExecutionContext.ts:293:12)
at IsolatedWorld.evaluate (node_modules/puppeteer-core/src/cdp/IsolatedWorld.ts:196:12)
at CdpFrame.evaluate (node_modules/puppeteer-core/src/api/Frame.ts:488:12)
at CdpPage.evaluate (node_modules/puppeteer-core/src/api/Page.ts:2320:12)
Код: Выделить всё
ReferenceError: _ref6 is not defined
at evaluate (evaluate at /workspaces/anotato-extension/tests/extension.test.js:159:32, :1:16)
at ExecutionContext.#evaluate (node_modules/puppeteer-core/src/cdp/ExecutionContext.ts:454:34)
at ExecutionContext.evaluate (node_modules/puppeteer-core/src/cdp/ExecutionContext.ts:293:12)
at IsolatedWorld.evaluate (node_modules/puppeteer-core/src/cdp/IsolatedWorld.ts:196:12)
at CdpFrame.evaluate (node_modules/puppeteer-core/src/api/Frame.ts:488:12)
at CdpPage.evaluate (node_modules/puppeteer-core/src/api/Page.ts:2320:12)
Тесты: 2 неудачно, 1 пройдено, всего 3
Снимки: всего 0
Время: 6,963 с
Выполнены все наборы тестов.
@DoviShamir ➜ /workspaces/anotato-extension (main) $
Подробнее здесь: https://stackoverflow.com/questions/797 ... tore-getdo