, so that
* hubspotform.js (which only loads later, inside the body, on pages that
* actually have a HubSpot Form component) can read the same values
* without duplicating them. hubspotform.js reads this lazily (at init
* time, not at parse time) so it never depends on this script having run
* first -- treat that as defense-in-depth, not a reason to relax this
* script's own placement in
.
*/
window.HS_CONFIG = window.HS_CONFIG || Object.freeze({
PORTAL_ID: '24131897',
GPC_FORM_GUID: '2b423b38-65e9-4811-8fdc-85e52e7d29d8',
DEFAULT_REGION: 'na1'
});
const PORTAL_ID = window.HS_CONFIG.PORTAL_ID;
const GPC_FORM_GUID = window.HS_CONFIG.GPC_FORM_GUID;
// ---------- Generic cookie helpers ----------
function getCookie(name) {
const match = document.cookie.match(
new RegExp('(^|;\\s*)' + name.replace(/[.*+?^{}()|[\]\\$]/g, '\\$&') + '=([^;]*)')
);
return match ? decodeURIComponent(match[2]) : null;
}
function setCookie(name, value, days) {
const expires = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();
document.cookie = name + '=' + encodeURIComponent(value) + '; expires=' + expires + '; path=/; SameSite=Lax; Secure;';
}
// ---------- OptanonConsent parsing ----------
//
// OptanonConsent is a URL-encoded, semi-structured query string, e.g.:
// isGpcEnabled=1&datestamp=2026-09-11T14:32:10Z&version=202401.1.0&
// groups=C0001%3A1%2CC0002%3A1%2CC0003%3A1%2CC0004%3A0&...
//
// Note: OneTrust's cookie format has changed across versions -- verify
// field names against your account's actual cookie before relying on
// this in prod.
function parseOptanonConsent() {
const raw = getCookie('OptanonConsent');
if (!raw) return null;
const params = new URLSearchParams(raw);
const groupsRaw = params.get('groups') || '';
const groups = {};
groupsRaw.split(',').forEach((pair) => {
const parts = pair.split(':');
const id = parts[0];
const status = parts[1];
if (id) groups[id] = status === '1';
});
return {
isGpcEnabled: params.get('isGpcEnabled') === '1',
datestamp: params.get('datestamp'),
groups: groups
};
}
// ---------- Status tracking cookie (keyed by email) ----------
//
// Keyed by email rather than a single flat record. On a shared device, a
// flat "last status sent" record gets overwritten by whoever used the
// browser most recently, causing one person's status to look "already
// sent" for someone else. Keying by email keeps each person's dedupe
// state separate even on a shared browser/cookie jar.
const STATUS_COOKIE_NAME = 'hs_gpc_status_records';
const STATUS_COOKIE_DAYS = 365;
function normalizeEmail(email) {
return String(email || '').trim().toLowerCase();
}
function getAllStatusRecords() {
const raw = getCookie(STATUS_COOKIE_NAME);
if (!raw) return {};
try {
return JSON.parse(raw) || {};
} catch (e) {
return {};
}
}
function getStatusRecordForEmail(email) {
const key = normalizeEmail(email);
if (!key) return null;
return getAllStatusRecords()[key] || null;
}
function setStatusRecordForEmail(email, lastSentGpcStatus, submissionStatus) {
const key = normalizeEmail(email);
if (!key) return;
const records = getAllStatusRecords();
records[key] = {
lastSentGpcStatus: lastSentGpcStatus,
lastUpdated: new Date().toISOString(),
submissionStatus: submissionStatus // "submitted" | "failed" | "pending"
};
// Cookies have a ~4KB practical limit -- prune oldest entries beyond a
// reasonable count so this doesn't grow unbounded on shared devices.
const MAX_RECORDS = 20;
const keys = Object.keys(records);
if (keys.length > MAX_RECORDS) {
const oldestFirst = keys.sort(
(a, b) => new Date(records[a].lastUpdated) - new Date(records[b].lastUpdated)
);
oldestFirst.slice(0, keys.length - MAX_RECORDS).forEach((k) => delete records[k]);
}
setCookie(STATUS_COOKIE_NAME, JSON.stringify(records), STATUS_COOKIE_DAYS);
}
// ---------- Email-identified submission path (preferred) ----------
async function maybeSubmitGpcStatusForEmail(email) {
const key = normalizeEmail(email);
if (!key) return;
const consent = parseOptanonConsent();
if (!consent) return; // OneTrust hasn't initialized yet
const currentGpcStatus = consent.isGpcEnabled === true;
const existing = getStatusRecordForEmail(key);
if (
existing &&
existing.lastSentGpcStatus === currentGpcStatus &&
existing.submissionStatus === 'submitted'
) {
return; // Already recorded this exact status for this specific person
}
setStatusRecordForEmail(key, currentGpcStatus, 'pending');
try {
await submitGpcStatusUpdate({ email: key, gpcStatus: currentGpcStatus });
setStatusRecordForEmail(key, currentGpcStatus, 'submitted');
} catch (err) {
setStatusRecordForEmail(key, currentGpcStatus, 'failed');
}
}
// ---------- hubspotutk-only background path (fallback, higher risk) ----------
//
// Browser-identified, not person-identified -- can misattribute status on
// a genuinely shared device. Kept as a fallback for visitors who never
// submit a form.
const HUBSPOTUTK_RETRY_ATTEMPTS = 5;
const HUBSPOTUTK_RETRY_DELAY_MS = 1000;
async function maybeSubmitGpcStatusBackground(retriesLeft = HUBSPOTUTK_RETRY_ATTEMPTS) {
const hubspotutk = getCookie('hubspotutk');
if (!hubspotutk) {
// hubspotutk is written by HubSpot's own tracking script, which only runs
// (and sets this cookie) after it processes the _hsq doNotTrack push from
// syncHubSpotDoNotTrack 鈥� it may not exist yet the instant consent
// resolves. Retry for a bounded window rather than giving up on one miss.
if (retriesLeft > 0) {
setTimeout(() => maybeSubmitGpcStatusBackground(retriesLeft - 1), HUBSPOTUTK_RETRY_DELAY_MS);
}
return;
}
const consent = parseOptanonConsent();
if (!consent) return;
const currentGpcStatus = consent.isGpcEnabled === true;
const dedupeKey = 'hutk:' + hubspotutk;
const existing = getStatusRecordForEmail(dedupeKey);
if (
existing &&
existing.lastSentGpcStatus === currentGpcStatus &&
existing.submissionStatus === 'submitted'
) {
return;
}
setStatusRecordForEmail(dedupeKey, currentGpcStatus, 'pending');
try {
await submitGpcStatusUpdate({ hubspotutk: hubspotutk, gpcStatus: currentGpcStatus });
setStatusRecordForEmail(dedupeKey, currentGpcStatus, 'submitted');
} catch (err) {
setStatusRecordForEmail(dedupeKey, currentGpcStatus, 'failed');
}
}
async function submitGpcStatusUpdate(opts) {
const email = opts.email;
const hubspotutk = opts.hubspotutk;
const gpcStatus = opts.gpcStatus;
const fields = [
{ name: 'consent_optout_gpc', value: String(gpcStatus) },
{ name: 'consent_last_updated_date_gpc', value: new Date().toISOString() }
];
if (email) fields.push({ name: 'email', value: email });
const response = await fetch(
'https://api.hsforms.com/submissions/v3/integration/submit/' + PORTAL_ID + '/' + GPC_FORM_GUID,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fields: fields,
context: hubspotutk ? { hutk: hubspotutk } : {}
})
}
);
if (!response.ok) throw new Error('HubSpot form submission failed: ' + response.status);
return response.json();
}
// Kept as a defensive fallback in case this account's OneTrust setup does
// dispatch it in some configurations 鈥� doesn't hurt to also listen.
function onOneTrustGroupsUpdated() {
maybeSubmitGpcStatusBackground();
}
window.addEventListener('OneTrustGroupsUpdated', onOneTrustGroupsUpdated);
if (window.OneTrust) {
// Fast path for when consent is already known (e.g. on a refresh, or a
// returning visitor) 鈥� avoids waiting on an event that may not fire again
// if consent isn't changing this load.
maybeSubmitGpcStatusBackground();
}
// ---------- HubSpot form submission hooks (email-identified, preferred) ----------
// Iframe embed (postMessage-based) -- also covers hbspt.forms.create(),
// since that renders the same underlying iframe and fires the same
// postMessage events.
window.addEventListener('message', (event) => {
if (!event.data || event.data.type !== 'hsFormCallback') return;
if (event.data.eventName !== 'onFormSubmitted') return;
const email = event.data.data && event.data.data.submissionValues
? event.data.data.submissionValues.email
: null;
if (email) {
maybeSubmitGpcStatusForEmail(email);
} else {
maybeSubmitGpcStatusBackground();
}
});
// HubSpot's non-iframe ("raw HTML") / v4 forms dispatch this CustomEvent
// on window. event.detail only carries { formId, instanceId } -- actual
// field values must be fetched via the async getFormFieldValues() call.
window.addEventListener('hs-form-event:on-submission:success', (event) => {
if (!window.HubSpotFormsV4) return;
HubSpotFormsV4.getFormFromEvent(event)
.getFormFieldValues()
.then((fieldValues) => {
// "0-1" is HubSpot's fixed objectTypeId for Contact -- forms with
// fields on multiple objects (e.g. Contact + Ticket) can have more
// than one "email" field, so target the Contact's explicitly.
const emailField = fieldValues.find((field) => field.name === '0-1/email');
const email = emailField ? emailField.value : null;
if (email) {
maybeSubmitGpcStatusForEmail(email);
} else {
maybeSubmitGpcStatusBackground();
}
});
});
})();
Whether your test center must make a change due to inclement weather or other unexpected reasons, ACT sends email updates Monday through Friday by 6:00 p.m. CT to notify you of changes in your registration, so you can take action.
Rescheduled Test Centers
Some last minute and unexpected issues may be resolved through make-up testing, where your test is rescheduled to a new test day. ACT works with test centers to determine if rescheduling is possible; You will be notified by email if this occurs. Within the email, you will be provided with additional information including a new date and location.
Cancelled Test Centers If your test center closes and ACT is unable to place you in an available test center nearby, you will need to adjust your registration. You will be placed into a CLOSED ACT TEST CENTER, and you will not be able to test at your previous test center. Follow the instruction on your ACT admission ticket - as outlined in your cancellation email - to make a change to your registration.
ACT Rescheduled Test Centers
National Test Rescheduled Test Centers
Last Updated:
Oct 19 2026 / 6:52 PM
International Test Centers
There are currently no rescheduled international test centers.
U.S. Test Centers
State, City
Location
Center Code
Alabama, BREWTON
COASTAL ALABAMA COMM COLLEGE-BREWTON
000390
Alabama, BREWTON
COASTAL ALABAMA COMM COLLEGE-BREWTON
000391
Florida, MILTON
MILTON HIGH SCHOOL
190850
Florida, PENSACOLA
PENSACOLA HIGH SCHOOL
195580
Florida, PENSACOLA
PENSACOLA HIGH SCHOOL
195581
Florida, PENSACOLA
WEST FLORIDA HIGH SCHOOL OF ADVANCED TECH
222770
Guam, BARRIGADA
ST JOHN'S SCHOOL
237940
Illinois, QUINCY
QUINCY SR HIGH SCHOOL
174660
Kentucky, LOUISVILLE
WAGGENER HIGH SCHOOL
176581
Kentucky, LOUISVILLE
WAGGENER HIGH SCHOOL
176580
Louisiana, OPELOUSAS
OPELOUSAS HIGH SCHOOL
192340
Maryland, RANDALLSTOWN
RANDALLSTOWN HIGH SCHOOL
212940
Massachusetts, BROCKTON
NEW HEIGHTS CHARTER SCHOOL-BROCKTON
263330
Massachusetts, MILFORD
MILFORD HIGH SCHOOL
224990
Montana, GREAT FALLS
CHARLES M RUSSELL HIGH SCHOOL
179590
New York, CLIFTON PARK
SHENENDEHOWA HIGH SCHOOL
199690
New York, SARATOGA SPRINGS
SARATOGA SPRINGS HIGH SCHOOL
196320
North Carolina, WHITEVILLE
CAROLINA ADVENTIST ACADEMY
258291
Oklahoma, DUNCAN
DUNCAN HIGH SCHOOL
183231
Oklahoma, DUNCAN
DUNCAN HIGH SCHOOL
183230
Oklahoma, TULSA
MEMORIAL HIGH SCHOOL
183411
Oklahoma, TULSA
MEMORIAL HIGH SCHOOL
183410
Oregon, BEND
SUMMIT HIGH SCHOOL
254120
Pennsylvania, GLEN ROCK
SUSQUEHANNOCK HIGH SCHOOL
233490
Tennessee, KNOXVILLE
BEARDEN HIGH SCHOOL
180390
Virginia, NEWPORT NEWS
DENBIGH HIGH SCHOOL
212280
This action will open a new window. Do you want to proceed?
Welcome to ACT
If you are accessing this site from outside the United States, Puerto Rico, or U.S. Territories, please proceed to the non-U.S. version of our website.