Skip to main content

Testing & Troubleshooting

You can check everything on this page from your browser's developer console, on any page where the Surfside tag is installed.

Check the current state​

surf('getConsent', console.log);

With no CMP and no GPC, you should see the default:

{ tracking: 'granted', identity: 'granted', signal: 'none', updatedAt: '1970-01-01T00:00:00.000Z' }

The 1970 date is expected: it means no decision has been made yet.

Find out which source decided​

getConsent shows the combined result, and combining hides detail. A source that was outvoted, and a source that never reported at all, both look the same from outside. To see what each source actually said:

['tcf', 'gpp', 'gpc', 'api'].forEach(function (source) {
surf('getConsentSignal', { source: source, callback: function (s) { console.log(source, s); } });
});
Result for a sourceMeaning
undefinedThe source has never reported. Its reader is turned off, hasn't been started, or is still waiting for the CMP.
tracking and identity both no-signalThe source reported, but expressed no preference
granted or deniedThe source's actual verdict

Check what is on the wire​

With the Surfside Inspector open, look at any event:

  • the io.surfside.consent/state context should match getConsent
  • with identity denied, the io.surfside.identity/* contexts and the user ID should be gone
  • with tracking denied, the cookie IDs (duid, sid) should be gone
  • when you change consent, an io.surfside.consent/signal event should appear

Simulate signals​

Use these on a test page or staging site. They stand in for a real CMP, so run them on a page without a live TCF or GPP CMP. Once the SDK has connected to a CMP, replacing window.__tcfapi or window.__gpp has no effect.

Global Privacy Control​

Object.defineProperty(Navigator.prototype, 'globalPrivacyControl', {
configurable: true,
get: function () { return true; }
});

The SDK picks this up within about 100 ms. Expect identity: 'denied', signal: 'gpc'.

IAB GPP​

This example opts a Virginia shopper out of sale and targeted advertising:

window.__gpp = function (command, callback) {
if (command !== 'addEventListener') return;
callback({
eventName: 'signalStatus',
data: 'ready',
pingData: {
signalStatus: 'ready',
gppString: 'DBABRg~BUUAAAA.QA',
applicableSections: [9],
parsedSections: {
usva: [{ SaleOptOut: 1, TargetedAdvertisingOptOut: 1, MspaCoveredTransaction: 1, MspaServiceProviderMode: 2 }]
},
cmpId: 42
}
}, true);
};

Expect identity: 'denied', signal: 'gpp', jurisdictions: ['usva']. To test a grant, set the opt-out fields to 2.

Set MspaServiceProviderMode to 2 while testing the opt-out fields. If you set it to 1, it denies identity by itself and you won't know which field caused the denial. Check the per-state fields too: using a field a state doesn't define gives no-signal, not a denial.

IAB TCF​

This example grants device storage (Purpose 1) but refuses profiling (Purpose 3):

window.__tcfapi = function (command, version, callback) {
if (command !== 'addEventListener') return;
callback({
eventStatus: 'useractioncomplete',
cmpStatus: 'loaded',
gdprApplies: true,
cmpId: 10,
purpose: { consents: { 1: true, 3: false } }
}, true);
};

Expect tracking: 'granted', identity: 'denied', signal: 'tcf'. Set 1: false to see tracking denied as well.

Your own banner​

surf('setConsent', { tracking: true, identity: false });
surf('getConsent', console.log);

Reset between tests​

Consent is remembered across page views, so clear it between tests:

localStorage.removeItem('surf_consent');
delete window.__gpp;
delete window.__tcfapi;
delete Navigator.prototype.globalPrivacyControl;
location.reload();

After the reload, getConsent should show signal: 'none'. If it shows anything else, something is still setting consent; use getConsentSignal to find out which source.

clearUserData does not clear consent. Remove surf_consent by name.

Deliberate behaviours​

These can look like bugs, but they're intentional.

setConsent({ identity: true }) doesn't lift a GPC, GPP or TCF denial. The most restrictive source always wins, and no source outranks another. A shopper who opted out through their browser or your CMP stays opted out until that same source changes.

GPC and GPP don't stop events being sent. They deny identity only. US opt-out rights cover the sale and sharing of personal data and targeted advertising; first-party measurement continues with identity removed. Only TCF Purpose 1 or setConsent({ tracking: false }) changes tracking.

Turning GPC off doesn't take effect straight away. Within a page view GPC can only switch on. The earlier opt-out is lifted once the SDK confirms GPC is really absent, on a page view where it stays absent for the full five-minute check. See Global Privacy Control.

A restored opt-out sends no consent change event. A decision restored from an earlier page view is enforced from the first event, but the shopper didn't decide anything on this page, so no change is recorded.

A CMP showing its banner doesn't deny anything. Until the shopper answers, an IAB CMP publishes a "banner shown" status with empty consents. The SDK waits for a real decision rather than reading the empty banner state as "reject all".

A slow CMP doesn't undo an earlier opt-out. If a shopper opted out through TCF or GPP on an earlier visit, that opt-out stays in force on later pages even before the CMP loads. Only the CMP publishing an actual grant lifts it.

Identity calls seem to do nothing. While identity is denied, setUser, setSurfId, identifyUser and setUserId* are held, not sent. They're replayed if identity is granted later on the same page. See Enforcement.

Editing surf_consent in developer tools has no effect. The stored value is protected against tampering, and an edited value is discarded. Use setConsent or the mocks above instead.

Common problems​

SymptomLikely causeFix
surf is not definedwave.js hasn't loaded yetWait for window.surf before calling it. See Where you can call them.
The tag stopped loading after you added consent codeYour page defines window.surf before wave.js runsRemove your own surf stub, and wait for wave.js to create it instead
getConsentSignal returns undefined for your CMP's sourceThe reader isn't runningOn edge.js or npm, call enableConsentSignals. On wave.js, ask Surfside to check that reader isn't set to false in your consentConfig.
The GPP source reports no-signal for a shopper who opted outThe CMP uses a field the state's section doesn't define, or applicableSections doesn't list the shopper's stateCheck the per-state fields and your CMP's jurisdiction settings
The TCF source reports no-signalgdprApplies is false, or the CMP hasn't published a decision yetExpected outside GDPR regions, or until the shopper answers the banner
Your accept button doesn't restore identityA GPC, GPP or TCF denial is still in forceExpected. See Deliberate behaviours.
A user ID appears on events while identity is denied (npm)setUserId imported from @surfside/browser-sdkImport it from @surfside/surfside-events
Consent isn't remembered between pageslocalStorage is blocked, or Surfside has set enabled: false for the sourceCheck browser storage settings, or ask Surfside to check your consentConfig