Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| acf642d7e6 | |||
| d5da2a91c8 | |||
| ebe664f984 | |||
| 9c7324ee92 | |||
| 0c8d9ea669 |
@@ -14,4 +14,4 @@ jobs:
|
|||||||
secrets:
|
secrets:
|
||||||
GOTIFY_URL: ${{ secrets.GOTIFY_URL }}
|
GOTIFY_URL: ${{ secrets.GOTIFY_URL }}
|
||||||
GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }}
|
GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }}
|
||||||
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
|
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'lassmichrein' }}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@
|
|||||||
"valid-id": "off",
|
"valid-id": "off",
|
||||||
"element-required-attributes": "off",
|
"element-required-attributes": "off",
|
||||||
"attribute-empty-style": "off",
|
"attribute-empty-style": "off",
|
||||||
"element-permitted-content": "off"
|
"element-permitted-content": "off",
|
||||||
|
"element-required-content": "off",
|
||||||
|
"element-permitted-parent": "off",
|
||||||
|
"no-implicit-close": "off",
|
||||||
|
"close-order": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
|||||||
alt={post.frontmatter.title}
|
alt={post.frontmatter.title}
|
||||||
fill
|
fill
|
||||||
priority
|
priority
|
||||||
|
quality={100}
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
style={{
|
style={{
|
||||||
@@ -133,13 +134,13 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
|||||||
<span>{getReadingTime(rawTextContent)} min read</span>
|
<span>{getReadingTime(rawTextContent)} min read</span>
|
||||||
{(new Date(post.frontmatter.date) > new Date() ||
|
{(new Date(post.frontmatter.date) > new Date() ||
|
||||||
post.frontmatter.public === false) && (
|
post.frontmatter.public === false) && (
|
||||||
<>
|
<>
|
||||||
<span className="w-1 h-1 bg-white/30 rounded-full" />
|
<span className="w-1 h-1 bg-white/30 rounded-full" />
|
||||||
<span className="px-2 py-0.5 border border-white/40 text-white/80 rounded uppercase tracking-widest text-[10px] md:text-xs font-bold">
|
<span className="px-2 py-0.5 border border-white/40 text-white/80 rounded uppercase tracking-widest text-[10px] md:text-xs font-bold">
|
||||||
Draft Preview
|
Draft Preview
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -170,13 +171,13 @@ export default async function BlogPost({ params }: BlogPostProps) {
|
|||||||
<span>{getReadingTime(rawTextContent)} min read</span>
|
<span>{getReadingTime(rawTextContent)} min read</span>
|
||||||
{(new Date(post.frontmatter.date) > new Date() ||
|
{(new Date(post.frontmatter.date) > new Date() ||
|
||||||
post.frontmatter.public === false) && (
|
post.frontmatter.public === false) && (
|
||||||
<>
|
<>
|
||||||
<span className="w-1 h-1 bg-neutral-300 rounded-full" />
|
<span className="w-1 h-1 bg-neutral-300 rounded-full" />
|
||||||
<span className="px-2 py-0.5 border border-orange-500/50 text-orange-600 rounded uppercase tracking-widest text-[10px] md:text-xs font-bold">
|
<span className="px-2 py-0.5 border border-orange-500/50 text-orange-600 rounded uppercase tracking-widest text-[10px] md:text-xs font-bold">
|
||||||
Draft Preview
|
Draft Preview
|
||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export async function getPostBySlug(slug: string, locale: string): Promise<PostD
|
|||||||
category: doc.category || '',
|
category: doc.category || '',
|
||||||
featuredImage:
|
featuredImage:
|
||||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||||
? doc.featuredImage.sizes?.card?.url || doc.featuredImage.url
|
? doc.featuredImage.url || doc.featuredImage.sizes?.card?.url
|
||||||
: null,
|
: null,
|
||||||
focalX:
|
focalX:
|
||||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||||
@@ -162,7 +162,7 @@ export async function getAllPosts(locale: string): Promise<PostData[]> {
|
|||||||
category: doc.category || '',
|
category: doc.category || '',
|
||||||
featuredImage:
|
featuredImage:
|
||||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||||
? doc.featuredImage.sizes?.card?.url || doc.featuredImage.url
|
? doc.featuredImage.url || doc.featuredImage.sizes?.card?.url
|
||||||
: null,
|
: null,
|
||||||
focalX:
|
focalX:
|
||||||
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
typeof doc.featuredImage === 'object' && doc.featuredImage !== null
|
||||||
|
|||||||
@@ -139,7 +139,7 @@
|
|||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"preinstall": "npx only-allow pnpm"
|
"preinstall": "npx only-allow pnpm"
|
||||||
},
|
},
|
||||||
"version": "2.2.6",
|
"version": "2.2.9",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@parcel/watcher",
|
"@parcel/watcher",
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ async function main() {
|
|||||||
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
page.on('console', (msg) => console.log('💻 BROWSER CONSOLE:', msg.text()));
|
||||||
|
page.on('pageerror', (error) => console.error('💻 BROWSER ERROR:', error.message));
|
||||||
|
page.on('requestfailed', (request) => {
|
||||||
|
console.error('💻 BROWSER REQUEST FAILED:', request.url(), request.failure()?.errorText);
|
||||||
|
});
|
||||||
|
|
||||||
// 3. Authenticate through Gatekeeper login form
|
// 3. Authenticate through Gatekeeper login form
|
||||||
console.log(`\n🛡️ Authenticating through Gatekeeper...`);
|
console.log(`\n🛡️ Authenticating through Gatekeeper...`);
|
||||||
try {
|
try {
|
||||||
@@ -109,6 +115,9 @@ async function main() {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait specifically for hydration logic to initialize the onSubmit handler
|
||||||
|
await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 2000)));
|
||||||
|
|
||||||
// Fill form fields
|
// Fill form fields
|
||||||
await page.type('input[name="name"]', 'Automated E2E Test');
|
await page.type('input[name="name"]', 'Automated E2E Test');
|
||||||
await page.type('input[name="email"]', 'testing@mintel.me');
|
await page.type('input[name="email"]', 'testing@mintel.me');
|
||||||
@@ -117,14 +126,24 @@ async function main() {
|
|||||||
'This is an automated test verifying the contact form submission.',
|
'This is an automated test verifying the contact form submission.',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Give state a moment to settle
|
||||||
|
await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 500)));
|
||||||
|
|
||||||
console.log(` Submitting Contact Form...`);
|
console.log(` Submitting Contact Form...`);
|
||||||
|
|
||||||
// Explicitly click submit and wait for navigation/state-change
|
// Explicitly click submit and wait for navigation/state-change
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForSelector('[role="alert"][aria-live="polite"]', { timeout: 15000 }),
|
page.waitForSelector('[role="alert"]', { timeout: 15000 }),
|
||||||
page.click('button[type="submit"]'),
|
page.click('button[type="submit"]'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const alertText = await page.$eval('[role="alert"]', (el) => el.textContent);
|
||||||
|
console.log(` Alert text: ${alertText}`);
|
||||||
|
|
||||||
|
if (alertText?.includes('Failed') || alertText?.includes('went wrong')) {
|
||||||
|
throw new Error(`Form submitted but showed error: ${alertText}`);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`✅ Contact Form submitted successfully! (Success state verified)`);
|
console.log(`✅ Contact Form submitted successfully! (Success state verified)`);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(`❌ Contact Form Test Failed: ${err.message}`);
|
console.error(`❌ Contact Form Test Failed: ${err.message}`);
|
||||||
@@ -147,6 +166,9 @@ async function main() {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait specifically for hydration logic to initialize the onSubmit handler
|
||||||
|
await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 2000)));
|
||||||
|
|
||||||
// In RequestQuoteForm, the email input is type="email" and message is a textarea.
|
// In RequestQuoteForm, the email input is type="email" and message is a textarea.
|
||||||
await page.type('form input[type="email"]', 'testing@mintel.me');
|
await page.type('form input[type="email"]', 'testing@mintel.me');
|
||||||
await page.type(
|
await page.type(
|
||||||
@@ -154,14 +176,24 @@ async function main() {
|
|||||||
'Automated request for product quote via E2E testing framework.',
|
'Automated request for product quote via E2E testing framework.',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Give state a moment to settle
|
||||||
|
await page.evaluate(() => new Promise((resolve) => setTimeout(resolve, 500)));
|
||||||
|
|
||||||
console.log(` Submitting Product Quote Form...`);
|
console.log(` Submitting Product Quote Form...`);
|
||||||
|
|
||||||
// Submit and wait for success state
|
// Submit and wait for success state
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.waitForSelector('[role="alert"][aria-live="polite"]', { timeout: 15000 }),
|
page.waitForSelector('[role="alert"]', { timeout: 15000 }),
|
||||||
page.click('form button[type="submit"]'),
|
page.click('form button[type="submit"]'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const alertText = await page.$eval('[role="alert"]', (el) => el.textContent);
|
||||||
|
console.log(` Alert text: ${alertText}`);
|
||||||
|
|
||||||
|
if (alertText?.includes('Failed') || alertText?.includes('went wrong')) {
|
||||||
|
throw new Error(`Form submitted but showed error: ${alertText}`);
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`✅ Product Quote Form submitted successfully! (Success state verified)`);
|
console.log(`✅ Product Quote Form submitted successfully! (Success state verified)`);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(`❌ Product Quote Form Test Failed: ${err.message}`);
|
console.error(`❌ Product Quote Form Test Failed: ${err.message}`);
|
||||||
@@ -189,11 +221,20 @@ async function main() {
|
|||||||
});
|
});
|
||||||
console.log(` ✅ Deleted submission: ${doc.id}`);
|
console.log(` ✅ Deleted submission: ${doc.id}`);
|
||||||
} catch (delErr: any) {
|
} catch (delErr: any) {
|
||||||
console.error(` ❌ Failed to delete submission ${doc.id}: ${delErr.message}`);
|
// Log but don't fail, 403s on Directus / Payload APIs for guest Gatekeeper sessions are normal
|
||||||
|
console.warn(
|
||||||
|
` ⚠️ Cleanup attempt on ${doc.id} returned an error, typically due to API Auth separation: ${delErr.message}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error(`❌ Cleanup failed: ${err.message}`);
|
if (err.response?.status === 403) {
|
||||||
|
console.warn(
|
||||||
|
` ⚠️ Cleanup fetch failed with 403 Forbidden. This is expected if the runner lacks admin API credentials. Test submissions remain in the database.`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error(` ❌ Cleanup fetch failed: ${err.message}`);
|
||||||
|
}
|
||||||
// Don't mark the whole test as failed just because cleanup failed
|
// Don't mark the whole test as failed just because cleanup failed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user