From d092455039b601dbded64b2106e99ca91b139933 Mon Sep 17 00:00:00 2001
From: profilesearch <222277199+profilesearch@users.noreply.github.com>
Date: Thu, 6 Aug 2026 03:28:08 -0700
Subject: [PATCH] [verified] fix: track completed Google signups
---
backend/api_core.py | 1 +
backend/test_deep_audit_regressions.py | 29 +++++++++++++++
frontend/index.html | 12 +++++--
frontend/tests/browser-regression.spec.js | 44 +++++++++++++++++++++++
4 files changed, 83 insertions(+), 3 deletions(-)
diff --git a/backend/api_core.py b/backend/api_core.py
index f515d27..be3f116 100644
--- a/backend/api_core.py
+++ b/backend/api_core.py
@@ -8108,6 +8108,7 @@ def _handle_routes(db):
ep = db.execute("SELECT * FROM employer_profiles WHERE user_id = ?", [user_id]).fetchone()
user_data['worker_profile'] = row_to_dict(wp)
user_data['employer_profile'] = row_to_dict(ep)
+ user_data['is_new_user'] = is_new_google_user
return json_response(user_data, 200 if existing else 201)
diff --git a/backend/test_deep_audit_regressions.py b/backend/test_deep_audit_regressions.py
index 9af1148..aa05f2c 100644
--- a/backend/test_deep_audit_regressions.py
+++ b/backend/test_deep_audit_regressions.py
@@ -71,6 +71,35 @@ def _request_api(self, method, path, payload=None, token=""):
self.module.handle_request()
return parse_cgi_output(out.getvalue())
+ def test_google_auth_reports_whether_the_account_was_created(self):
+ os.environ["GOOGLE_CLIENT_ID"] = "google-client-under-test"
+ verified_identity = {
+ "aud": "google-client-under-test",
+ "iss": "https://accounts.google.com",
+ "email": "new-google-user@example.com",
+ "email_verified": "true",
+ "name": "New Google User",
+ "sub": "google-sub-under-test",
+ }
+ response = mock.MagicMock()
+ response.__enter__.return_value.read.return_value = json.dumps(verified_identity).encode("utf-8")
+
+ with (
+ mock.patch.object(self.module.urllib.request, "urlopen", return_value=response),
+ mock.patch.object(self.module, "send_welcome_email"),
+ ):
+ created_status, created = self._request_api(
+ "POST", "/auth/google", {"credential": "new-google-credential"}
+ )
+ existing_status, existing = self._request_api(
+ "POST", "/auth/google", {"credential": "returning-google-credential"}
+ )
+
+ self.assertEqual(created_status, 201, created)
+ self.assertIs(created["is_new_user"], True)
+ self.assertEqual(existing_status, 200, existing)
+ self.assertIs(existing["is_new_user"], False)
+
def test_service_mutations_enforce_checkout_compatible_delivery_days(self):
token = "tok-service-delivery"
db = self.module.get_db()
diff --git a/frontend/index.html b/frontend/index.html
index 9f3ddfb..822b01a 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -362,10 +362,16 @@
Common agent-delegated work
function handleGoogleCredential(response) {
api('/auth/google', { method: 'POST', body: { credential: response.credential } })
.then(data => {
- const { token, ...user } = data;
+ const { token, is_new_user: isNewUser, ...user } = data;
saveSession(token, user);
- toast('Welcome to GoHireHumans!', 'success');
- trackRecommendedEvent('login', { method: 'google' });
+ if (isNewUser) {
+ trackEvent('signup_completed', { source: 'google_auth', method: 'google' });
+ trackRecommendedEvent('sign_up', { method: 'google', source: 'google_auth' });
+ toast('Account created! Welcome to GoHireHumans.', 'success');
+ } else {
+ trackRecommendedEvent('login', { method: 'google' });
+ toast('Welcome back!', 'success');
+ }
navigate('#/');
})
.catch(err => toast(err.message || 'Google sign-in failed', 'error'));
diff --git a/frontend/tests/browser-regression.spec.js b/frontend/tests/browser-regression.spec.js
index 0ca898c..03c2ca7 100644
--- a/frontend/tests/browser-regression.spec.js
+++ b/frontend/tests/browser-regression.spec.js
@@ -239,6 +239,50 @@ test.describe('GoHireHumans public/browser regression suite', () => {
expect(call[2]).not.toHaveProperty('campaign');
});
+ test('Google auth distinguishes completed signup from returning login', async ({ page }) => {
+ await setupDeterministicLocalPage(page);
+ await page.goto('/', { waitUntil: 'domcontentloaded' });
+
+ const eventsFor = async isNewUser => page.evaluate(async newAccount => {
+ const events = [];
+ window.gtag = (...args) => events.push(args);
+ window.api = async path => {
+ if (path !== '/auth/google') throw new Error(`Unexpected API path: ${path}`);
+ return {
+ id: newAccount ? 701 : 702,
+ email: newAccount ? 'new-google@example.test' : 'returning-google@example.test',
+ name: newAccount ? 'New Google User' : 'Returning Google User',
+ token: 'google-auth-test-token',
+ is_new_user: newAccount,
+ };
+ };
+ window.saveSession = () => {};
+ window.toast = () => {};
+ window.navigate = () => {};
+ handleGoogleCredential({ credential: 'google-credential-under-test' });
+ for (let attempt = 0; attempt < 20 && events.length === 0; attempt += 1) {
+ await new Promise(resolve => setTimeout(resolve, 10));
+ }
+ return events
+ .filter(args => args[0] === 'event')
+ .map(args => ({ name: args[1], params: args[2] }));
+ }, isNewUser);
+
+ const newAccountEvents = await eventsFor(true);
+ expect(newAccountEvents.map(event => event.name)).toEqual(['signup_completed', 'sign_up']);
+ expect(newAccountEvents.find(event => event.name === 'signup_completed')?.params).toMatchObject({
+ ui_source: 'google_auth',
+ method: 'google',
+ });
+ expect(newAccountEvents.map(event => event.name)).not.toContain('login');
+
+ const returningEvents = await eventsFor(false);
+ expect(returningEvents.map(event => event.name)).toEqual(['login']);
+ expect(returningEvents[0]?.params).toMatchObject({ method: 'google' });
+ expect(returningEvents.map(event => event.name)).not.toContain('sign_up');
+ expect(returningEvents.map(event => event.name)).not.toContain('signup_completed');
+ });
+
test('application submission failure stays visible and retryable', async ({ page }) => {
await setupDeterministicLocalPage(page);
await page.goto('/', { waitUntil: 'domcontentloaded' });