Compare commits

...

3 Commits

4 changed files with 66 additions and 4 deletions

View File

@@ -69,6 +69,7 @@ export const EndScreenForm = ({
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!endingCard.headline?.default || endingCard.headline.default.trim() === ""}
isExternalUrlsAllowed={isExternalUrlsAllowed}
/>
<div>
{endingCard.subheader !== undefined && (
@@ -87,6 +88,7 @@ export const EndScreenForm = ({
locale={locale}
isStorageConfigured={isStorageConfigured}
autoFocus={!endingCard.subheader?.default || endingCard.subheader.default.trim() === ""}
isExternalUrlsAllowed={isExternalUrlsAllowed}
/>
</div>
</div>

View File

@@ -27,6 +27,7 @@ interface PictureSelectionFormProps {
isInvalid: boolean;
locale: TUserLocale;
isStorageConfigured: boolean;
isExternalUrlsAllowed?: boolean;
}
export const PictureSelectionForm = ({
@@ -39,6 +40,7 @@ export const PictureSelectionForm = ({
isInvalid,
locale,
isStorageConfigured = true,
isExternalUrlsAllowed,
}: PictureSelectionFormProps): JSX.Element => {
const environmentId = localSurvey.environmentId;
const surveyLanguageCodes = extractLanguageCodes(localSurvey.languages);
@@ -88,6 +90,7 @@ export const PictureSelectionForm = ({
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
isExternalUrlsAllowed={isExternalUrlsAllowed}
autoFocus={!element.headline?.default || element.headline.default.trim() === ""}
/>
<div ref={parent}>
@@ -106,6 +109,7 @@ export const PictureSelectionForm = ({
setSelectedLanguageCode={setSelectedLanguageCode}
locale={locale}
isStorageConfigured={isStorageConfigured}
isExternalUrlsAllowed={isExternalUrlsAllowed}
autoFocus={!element.subheader?.default || element.subheader.default.trim() === ""}
/>
</div>

View File

@@ -115,7 +115,7 @@ export const setup = async (
const expiresAt = existingConfig.status.expiresAt;
if (expiresAt && isNowExpired(new Date(expiresAt))) {
if (expiresAt && !isNowExpired(new Date(expiresAt))) {
console.error("🧱 Formbricks - Error state is not expired, skipping initialization");
return okVoid();
}

View File

@@ -6,7 +6,7 @@ import { addCleanupEventListeners, addEventListeners } from "@/lib/common/event-
import { Logger } from "@/lib/common/logger";
import { handleErrorOnFirstSetup, setup, tearDown } from "@/lib/common/setup";
import { setIsSetup } from "@/lib/common/status";
import { filterSurveys, isNowExpired } from "@/lib/common/utils";
import { filterSurveys, getIsDebug, isNowExpired } from "@/lib/common/utils";
import type * as Utils from "@/lib/common/utils";
import { fetchEnvironmentState } from "@/lib/environment/state";
import { DEFAULT_USER_STATE_NO_USER_ID } from "@/lib/user/state";
@@ -56,6 +56,7 @@ vi.mock("@/lib/common/utils", async (importOriginal) => {
...originalModule,
filterSurveys: vi.fn(),
isNowExpired: vi.fn(),
getIsDebug: vi.fn(),
};
});
@@ -86,6 +87,7 @@ describe("setup.ts", () => {
getInstanceConfigMock = vi.spyOn(Config, "getInstance");
getInstanceLoggerMock = vi.spyOn(Logger, "getInstance").mockReturnValue(mockLogger as unknown as Logger);
(getIsDebug as unknown as Mock).mockReturnValue(false);
});
afterEach(() => {
@@ -117,7 +119,8 @@ describe("setup.ts", () => {
}
});
test("skips setup if existing config is in error state and not expired", async () => {
test("skips setup if existing config is in error state and not expired (debug mode)", async () => {
(getIsDebug as unknown as Mock).mockReturnValue(true);
const mockConfig = {
get: vi.fn().mockReturnValue({
environmentId: "env_123",
@@ -131,7 +134,7 @@ describe("setup.ts", () => {
getInstanceConfigMock.mockReturnValue(mockConfig as unknown as Config);
(isNowExpired as unknown as Mock).mockReturnValue(true);
(isNowExpired as unknown as Mock).mockReturnValue(false); // Not expired
const result = await setup({ environmentId: "env_123", appUrl: "https://my.url" });
expect(result.ok).toBe(true);
@@ -140,6 +143,59 @@ describe("setup.ts", () => {
);
});
test("skips initialization if error state is active (not expired)", async () => {
(getIsDebug as unknown as Mock).mockReturnValue(false);
const mockConfig = {
get: vi.fn().mockReturnValue({
environmentId: "env_123",
appUrl: "https://my.url",
environment: {},
user: { data: {}, expiresAt: null },
status: { value: "error", expiresAt: new Date(Date.now() + 10000) },
}),
resetConfig: vi.fn(),
};
getInstanceConfigMock.mockReturnValue(mockConfig as unknown as Config);
(isNowExpired as unknown as Mock).mockReturnValue(false); // Time is NOT up
const result = await setup({ environmentId: "env_123", appUrl: "https://my.url" });
expect(result.ok).toBe(true);
// Should NOT fetch environment or user state
expect(fetchEnvironmentState).not.toHaveBeenCalled();
expect(mockConfig.resetConfig).not.toHaveBeenCalled();
});
test("continues initialization if error state is expired", async () => {
(getIsDebug as unknown as Mock).mockReturnValue(false);
const mockConfig = {
get: vi.fn().mockReturnValue({
environmentId: "env_123",
appUrl: "https://my.url",
environment: { data: { surveys: [] }, expiresAt: new Date() },
user: { data: {}, expiresAt: null },
status: { value: "error", expiresAt: new Date(Date.now() - 10000) },
}),
update: vi.fn(),
};
getInstanceConfigMock.mockReturnValue(mockConfig as unknown as Config);
(isNowExpired as unknown as Mock).mockReturnValue(true); // Time IS up
// Mock successful fetch to allow setup to proceed
(fetchEnvironmentState as unknown as Mock).mockResolvedValueOnce({
ok: true,
data: { data: { surveys: [] }, expiresAt: new Date() },
});
(filterSurveys as unknown as Mock).mockReturnValue([]);
const result = await setup({ environmentId: "env_123", appUrl: "https://my.url" });
expect(result.ok).toBe(true);
expect(fetchEnvironmentState).toHaveBeenCalled();
});
test("uses existing config if environmentId/appUrl match, checks for expiration sync", async () => {
const mockConfig = {
get: vi.fn().mockReturnValue({