From 3261b0a9a7f2c9cda2ea1147522de2070c8b91bb Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Fri, 19 Jul 2024 11:20:21 +0200 Subject: [PATCH 01/24] updated the scraper model - frontend --- wee/frontend/src/app/models/ScraperModels.ts | 85 +++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/wee/frontend/src/app/models/ScraperModels.ts b/wee/frontend/src/app/models/ScraperModels.ts index 34fe6decd..73314b50e 100644 --- a/wee/frontend/src/app/models/ScraperModels.ts +++ b/wee/frontend/src/app/models/ScraperModels.ts @@ -11,6 +11,7 @@ export interface ScraperResult { time:number; addresses: string[]; screenshot: string; + seoAnalysis: SeoAnalysis; } export interface ErrorResponse { @@ -21,7 +22,6 @@ export interface ErrorResponse { timestamp: string; path: string; } - } export interface RobotsResponse { @@ -58,6 +58,89 @@ export interface ContactInfo { socialLinks: string[] } +export interface SeoAnalysis { + XMLSitemapAnalysis: XMLSitemapAnalysis; + canonicalTagAnalysis: CanonicalTagAnalysis; + headingAnalysis: HeadingAnalysis; + imageAnalysis: ImageAnalysis; + indexabilityAnalysis: IndexabilityAnalysis; + internalLinksAnalysis: InternalLinksAnalysis; + metaDescriptionAnalysis: MetaDescriptionAnalysis; + mobileFriendlinessAnalysis: MobileFriendlinessAnalysis; + structuredDataAnalysis: StructuredDataAnalysis; + titleTagsAnalysis: TitleTagsAnalysis; + uniqueContentAnalysis: UniqueContentAnalysis; +} + +export interface XMLSitemapAnalysis { + isSitemapValid: boolean; + recommendations: string; +} + +export interface CanonicalTagAnalysis { + canonicalTag: string; + isCanonicalTagPresent: boolean; + recommendations: string; +} + +export interface HeadingAnalysis { + count: number; + headings: string[]; + recommendations: string; +} + +export interface ImageAnalysis { + errorUrls: string[]; + missingAltTextCount: number; + nonOptimizedCount: number; + reasonsMap: ReasonsMap; + recommendations: string; + totalImages: number; +} + +export interface ReasonsMap { + format: string[]; + other: string[]; + size: string[]; +} + +export interface IndexabilityAnalysis { + isIndexable: boolean; + recommendations: string; +} + +export interface InternalLinksAnalysis { + recommendations: string; + totalLinks: number; + uniqueLinks: number; +} + +export interface MetaDescriptionAnalysis { + length: number; + recommendations: string; + titleTag: string; +} + +export interface MobileFriendlinessAnalysis { + isResponsive: boolean; + recommendations: string; +} + +export interface StructuredDataAnalysis { + count: number; + recommendations: string; +} + +export interface TitleTagsAnalysis { + length: number; + metaDescription: string; + recommendations: string; +} + +export interface UniqueContentAnalysis { + +} + export interface Summary { domainStatus: number[]; domainErrorStatus: number; From 7593795a7b6356c3133dde126e82b28052ac2fb3 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Fri, 19 Jul 2024 11:39:08 +0200 Subject: [PATCH 02/24] added titleTagAnalysis (very basic UI) --- wee/frontend/src/app/(pages)/results/page.tsx | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index cc3a6015b..fe295bfee 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -30,6 +30,12 @@ interface SummaryInfo { description: string; } +interface TitleTags { + length: number; + metaDescription: string; + recommendations: string; +} + export default function Results() { return ( Loading...}> @@ -67,6 +73,7 @@ function ResultsComponent() { const [emails, setEmails] = useState([]); const [phones, setPhones] = useState([]); const [socialLinks, setSocialLinks] = useState([]); + const [titleTagsAnalysis, setTitleTagAnalysis] = useState(); useEffect(() => { if (url) { @@ -102,6 +109,7 @@ function ResultsComponent() { setEmails(urlResults[0].contactInfo.emails); setPhones(urlResults[0].contactInfo.phones); setSocialLinks(urlResults[0].contactInfo.socialLinks); + setTitleTagAnalysis(urlResults[0].seoAnalysis.titleTagsAnalysis); } } } @@ -702,7 +710,50 @@ function ResultsComponent() { - seo + {/* Keyword Analysis */} +
+

+ Keyword Analysis + +

+
+ + {/* Onpage Analysis */} +
+ {/* Heading */} +

+ On-Page Analysis + +

+ + {/* Title Tags */} +
+

Title Tags

+

Metadata description: {titleTagsAnalysis?.metaDescription}

+

Length: {titleTagsAnalysis?.length}

+

Recommendations: {titleTagsAnalysis?.recommendations}

+
+
+ + {/* Technical Analysis */} +
+

+ Technical Analysis + +

+
From 00576d86e6f8b8d43ecb67bfc1cdae0c3ee5a714 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Mon, 22 Jul 2024 08:58:53 +0200 Subject: [PATCH 03/24] uniquecontent analysis and error models (frontend) --- wee/frontend/src/app/models/ScraperModels.ts | 34 +++++++++++++------ .../src/seo-analysis/seo-analysis.service.ts | 2 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/wee/frontend/src/app/models/ScraperModels.ts b/wee/frontend/src/app/models/ScraperModels.ts index 73314b50e..0ed5185f4 100644 --- a/wee/frontend/src/app/models/ScraperModels.ts +++ b/wee/frontend/src/app/models/ScraperModels.ts @@ -59,17 +59,17 @@ export interface ContactInfo { } export interface SeoAnalysis { - XMLSitemapAnalysis: XMLSitemapAnalysis; - canonicalTagAnalysis: CanonicalTagAnalysis; - headingAnalysis: HeadingAnalysis; - imageAnalysis: ImageAnalysis; - indexabilityAnalysis: IndexabilityAnalysis; - internalLinksAnalysis: InternalLinksAnalysis; - metaDescriptionAnalysis: MetaDescriptionAnalysis; - mobileFriendlinessAnalysis: MobileFriendlinessAnalysis; - structuredDataAnalysis: StructuredDataAnalysis; - titleTagsAnalysis: TitleTagsAnalysis; - uniqueContentAnalysis: UniqueContentAnalysis; + XMLSitemapAnalysis: XMLSitemapAnalysis | SEOError; // tech + canonicalTagAnalysis: CanonicalTagAnalysis | SEOError; // tech + headingAnalysis: HeadingAnalysis | SEOError; // on page + imageAnalysis: ImageAnalysis | SEOError; // on page + indexabilityAnalysis: IndexabilityAnalysis | SEOError; // tech + internalLinksAnalysis: InternalLinksAnalysis | SEOError; // on page + metaDescriptionAnalysis: MetaDescriptionAnalysis | SEOError; // on page + mobileFriendlinessAnalysis: MobileFriendlinessAnalysis | SEOError; // tech + structuredDataAnalysis: StructuredDataAnalysis | SEOError; // tech + titleTagsAnalysis: TitleTagsAnalysis | SEOError; // on page + uniqueContentAnalysis: UniqueContentAnalysis | SEOError; // on page } export interface XMLSitemapAnalysis { @@ -138,7 +138,19 @@ export interface TitleTagsAnalysis { } export interface UniqueContentAnalysis { + recommendations: string; + textLength: number; + uniqueWordsPercentage: number; + repeatedWords: RepeatedWords[] +} + +export interface RepeatedWords { + word: string; + count: number; +} +export interface SEOError { + error: string; } export interface Summary { diff --git a/wee/webscraper/src/seo-analysis/seo-analysis.service.ts b/wee/webscraper/src/seo-analysis/seo-analysis.service.ts index 503392468..760a28dcc 100644 --- a/wee/webscraper/src/seo-analysis/seo-analysis.service.ts +++ b/wee/webscraper/src/seo-analysis/seo-analysis.service.ts @@ -34,7 +34,7 @@ export class SeoAnalysisService { this.analyzeTitleTag(htmlContent), this.analyzeHeadings(htmlContent), this.analyzeImageOptimization( url), - this.analyzeImageOptimization( htmlContent), + this.analyzeContentQuality( htmlContent), this.analyzeInternalLinks( htmlContent), this.analyzeSiteSpeed(url), this.analyzeMobileFriendliness(url), From c7ed36280e6e1a9d589e3a1573368898aca01c6e Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Mon, 22 Jul 2024 09:37:49 +0200 Subject: [PATCH 04/24] added basic UI for on page analysis --- wee/frontend/src/app/(pages)/results/page.tsx | 49 ++++++++++++++++--- wee/frontend/src/app/models/ScraperModels.ts | 12 ++--- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index fe295bfee..cbe9cc385 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -19,6 +19,7 @@ import { InfoPopOver } from '../../components/InfoPopOver'; import jsPDF from 'jspdf'; import { saveReport } from '../../services/SaveReportService'; import { Metadata, ErrorResponse } from '../../models/ScraperModels'; +import { FiCheck, FiSearch, FiEye, FiSmartphone, FiClock } from "react-icons/fi"; interface Classifications { label: string; @@ -712,7 +713,7 @@ function ResultsComponent() { {/* Keyword Analysis */}
-

+

Keyword Analysis {/* Heading */} -

+

On-Page Analysis {/* Title Tags */} -
-

Title Tags

-

Metadata description: {titleTagsAnalysis?.metaDescription}

-

Length: {titleTagsAnalysis?.length}

-

Recommendations: {titleTagsAnalysis?.recommendations}

+
+ {/* Heading */} +
+
+ +
+
+

+ Title Tags +

+
+
+ + {/* Content */} +
+
+
+ Metadata Description +
+

{titleTagsAnalysis?.metaDescription}

+
+ +
+
+ Length +
+

{titleTagsAnalysis?.length}

+
+ +
+
+ Recommendations: +
+

{titleTagsAnalysis?.recommendations}

+
+
+
{/* Technical Analysis */}
-

+

Technical Analysis Date: Mon, 22 Jul 2024 21:38:03 +0200 Subject: [PATCH 05/24] basic image analysis layout --- wee/frontend/src/app/(pages)/results/page.tsx | 161 +++++++++++++++++- 1 file changed, 154 insertions(+), 7 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index cbe9cc385..3ed8b4c30 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -19,7 +19,7 @@ import { InfoPopOver } from '../../components/InfoPopOver'; import jsPDF from 'jspdf'; import { saveReport } from '../../services/SaveReportService'; import { Metadata, ErrorResponse } from '../../models/ScraperModels'; -import { FiCheck, FiSearch, FiEye, FiSmartphone, FiClock } from "react-icons/fi"; +import { FiCheck, FiSearch, FiEye, FiSmartphone, FiClock, FiImage, FiAnchor } from "react-icons/fi"; interface Classifications { label: string; @@ -36,6 +36,26 @@ interface TitleTags { metaDescription: string; recommendations: string; } +interface Headings { + count: number; + headings: string[]; + recommendations: string; +} + +interface Images { + errorUrls: string[]; + missingAltTextCount: number; + nonOptimizedCount: number; + reasonsMap: ReasonsMap; + recommendations: string; + totalImages: number; +} + +interface ReasonsMap { + format: string[]; + other: string[]; + size: string[]; +} export default function Results() { return ( @@ -75,6 +95,8 @@ function ResultsComponent() { const [phones, setPhones] = useState([]); const [socialLinks, setSocialLinks] = useState([]); const [titleTagsAnalysis, setTitleTagAnalysis] = useState(); + const [headingAnalysis, setHeadingAnalysis] = useState(); + const [imagesAnalysis, setImageAnalysis] = useState(); useEffect(() => { if (url) { @@ -111,6 +133,8 @@ function ResultsComponent() { setPhones(urlResults[0].contactInfo.phones); setSocialLinks(urlResults[0].contactInfo.socialLinks); setTitleTagAnalysis(urlResults[0].seoAnalysis.titleTagsAnalysis); + setHeadingAnalysis(urlResults[0].seoAnalysis.headingAnalysis); + setImageAnalysis(urlResults[0].seoAnalysis.imageAnalysis); } } } @@ -234,10 +258,7 @@ function ResultsComponent() { const filename = cleanFilename(url); doc.save(`${filename}.pdf`); }; - - - // Pagination Logic const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(16); @@ -726,7 +747,7 @@ function ResultsComponent() { {/* Onpage Analysis */}
{/* Heading */} -

+

On-Page Analysis

+ {/* Heading Analysis */} +
+ {/* Heading */} +
+
+ +
+
+

+ Headings +

+
+
+ + {/* Content */} +
+
+
+ List of Headings +
+
    + {headingAnalysis?.headings.map((heading) => ( +
  • {heading}
  • + ))} +
+
+ +
+
+ Count +
+

{headingAnalysis?.count}

+
+ +
+
+ Recommendations +
+

{headingAnalysis?.recommendations}

+
+
+
+ + {/* Image Analysis */} +
+ {/* Heading */} +
+
+ +
+
+

+ Images +

+
+
+ + {/* Content */} +
+
+
+ Total images +
+

+ {imagesAnalysis?.totalImages} +

+
+ +
+
+ Missing alternative text count +
+

+ {imagesAnalysis?.missingAltTextCount} +

+
+ +
+
+ Non-optimized images +
+

{imagesAnalysis?.nonOptimizedCount}

+
+ +
+
+ The format of the following URLs are incorrect +
+

+ {imagesAnalysis?.reasonsMap.format.map((formatUrl) => ( +

{formatUrl}

+ ))} +

+
+ +
+
+ The size of the following URLs are to big +
+

+ {imagesAnalysis?.reasonsMap.size.map((reasonUrl) => ( +

{reasonUrl}

+ ))} +

+
+ +
+
+ The following images have some problems: +
+

+ {imagesAnalysis?.reasonsMap.other.map((otherUrl) => ( +

{otherUrl}

+ ))} +

+
+ +
+
+ Recommendations +
+

{imagesAnalysis?.recommendations}

+
+
+
+ {/* Title Tags */} -
+
{/* Heading */}
@@ -767,7 +914,7 @@ function ResultsComponent() {
- Recommendations: + Recommendations

{titleTagsAnalysis?.recommendations}

From 9d2e898f332c631990765d572b18012de8664a95 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Mon, 22 Jul 2024 23:06:04 +0200 Subject: [PATCH 06/24] make image links clickable and overall ui images analysis improvements --- wee/frontend/src/app/(pages)/results/page.tsx | 89 +++++++++++-------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 3ed8b4c30..2afa2cfdf 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -5,7 +5,7 @@ import { Button, Tabs, Tab, TableHeader, TableColumn, TableBody, TableRow, TableCell, Dropdown, DropdownTrigger, DropdownMenu, DropdownItem, - Modal, ModalContent, ModalBody, useDisclosure, Input, ModalFooter, + Modal, ModalContent, ModalBody, useDisclosure, Input, ModalFooter, Link } from '@nextui-org/react'; import { FiShare, FiDownload, FiSave } from "react-icons/fi"; import { Chip } from '@nextui-org/react'; @@ -815,65 +815,76 @@ function ResultsComponent() { {/* Content */}
-
-
- Total images -
-

- {imagesAnalysis?.totalImages} -

-
+ {/* Count */} +
+
+
+ {imagesAnalysis?.totalImages} +
+
+ Total Images +
+
-
-
- Missing alternative text count -
-

- {imagesAnalysis?.missingAltTextCount} -

-
+
+
+ {imagesAnalysis?.missingAltTextCount} +
+
+ Missing Alt. Text +
+
-
-
- Non-optimized images -
-

{imagesAnalysis?.nonOptimizedCount}

+
+
+ {imagesAnalysis?.nonOptimizedCount} +
+
+ Non-Optimized Images +
+
-
+
The format of the following URLs are incorrect
-

- {imagesAnalysis?.reasonsMap.format.map((formatUrl) => ( -

{formatUrl}

+
+ {imagesAnalysis?.reasonsMap.format.map((formatUrl, index) => ( +

+ {formatUrl} +

))} -

+
-
+
The size of the following URLs are to big
-

- {imagesAnalysis?.reasonsMap.size.map((reasonUrl) => ( -

{reasonUrl}

+
+ {imagesAnalysis?.reasonsMap.size.map((reasonUrl, index) => ( +

+ {reasonUrl} +

))} -

+
-
+
- The following images have some problems: + The following images have some other problems
-

- {imagesAnalysis?.reasonsMap.other.map((otherUrl) => ( -

{otherUrl}

+
+ {imagesAnalysis?.reasonsMap.other.map((otherUrl, index) => ( +

+ {otherUrl} +

))} -

+
-
+
Recommendations
From 3d931390e73205ccd21f0ea01673d96997a5ff99 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Tue, 23 Jul 2024 07:21:22 +0200 Subject: [PATCH 07/24] added styling for recommendations --- wee/frontend/src/app/(pages)/results/page.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 2afa2cfdf..498597daa 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -790,7 +790,7 @@ function ResultsComponent() {

{headingAnalysis?.count}

-
+
Recommendations
@@ -818,7 +818,7 @@ function ResultsComponent() { {/* Count */}
-
+
{imagesAnalysis?.totalImages}
@@ -827,7 +827,7 @@ function ResultsComponent() {
-
+
{imagesAnalysis?.missingAltTextCount}
@@ -836,7 +836,7 @@ function ResultsComponent() {
-
+
{imagesAnalysis?.nonOptimizedCount}
@@ -849,7 +849,7 @@ function ResultsComponent() {
The format of the following URLs are incorrect
-
+
{imagesAnalysis?.reasonsMap.format.map((formatUrl, index) => (

{formatUrl} @@ -862,7 +862,7 @@ function ResultsComponent() {

The size of the following URLs are to big
-
+
{imagesAnalysis?.reasonsMap.size.map((reasonUrl, index) => (

{reasonUrl} @@ -875,7 +875,7 @@ function ResultsComponent() {

The following images have some other problems
-
+
{imagesAnalysis?.reasonsMap.other.map((otherUrl, index) => (

{otherUrl} @@ -884,7 +884,7 @@ function ResultsComponent() {

-
+
Recommendations
@@ -923,7 +923,7 @@ function ResultsComponent() {

{titleTagsAnalysis?.length}

-
+
Recommendations
From 594b4ee5b940a1687aeb204eab2b8089f3c9ddf5 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Tue, 23 Jul 2024 07:48:29 +0200 Subject: [PATCH 08/24] added internal linking --- wee/frontend/src/app/(pages)/results/page.tsx | 67 +++++++++++++++++-- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 498597daa..906f1d7f9 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -19,7 +19,7 @@ import { InfoPopOver } from '../../components/InfoPopOver'; import jsPDF from 'jspdf'; import { saveReport } from '../../services/SaveReportService'; import { Metadata, ErrorResponse } from '../../models/ScraperModels'; -import { FiCheck, FiSearch, FiEye, FiSmartphone, FiClock, FiImage, FiAnchor } from "react-icons/fi"; +import { FiCheck, FiSearch, FiEye, FiSmartphone, FiClock, FiImage, FiAnchor, FiLink } from "react-icons/fi"; interface Classifications { label: string; @@ -57,6 +57,12 @@ interface ReasonsMap { size: string[]; } +interface InternalLinking { + recommendations: string; + totalLinks: number; + uniqueLinks: number; +} + export default function Results() { return ( Loading...
}> @@ -97,6 +103,7 @@ function ResultsComponent() { const [titleTagsAnalysis, setTitleTagAnalysis] = useState(); const [headingAnalysis, setHeadingAnalysis] = useState(); const [imagesAnalysis, setImageAnalysis] = useState(); + const [internalLinkingAnalysis, setInternalLinkingAnalysis] = useState(); useEffect(() => { if (url) { @@ -135,6 +142,7 @@ function ResultsComponent() { setTitleTagAnalysis(urlResults[0].seoAnalysis.titleTagsAnalysis); setHeadingAnalysis(urlResults[0].seoAnalysis.headingAnalysis); setImageAnalysis(urlResults[0].seoAnalysis.imageAnalysis); + setInternalLinkingAnalysis(urlResults[0].seoAnalysis.internalLinksAnalysis); } } } @@ -733,7 +741,7 @@ function ResultsComponent() { {/* Keyword Analysis */} -
+ {/*

Keyword Analysis

-
+
*/} {/* Onpage Analysis */}
- {/* Heading */} + + {/* Onpage Analysis Heading */}

On-Page Analysis

+ {/* Internal Linking Analysis */} +
+ {/* Heading */} +
+
+ +
+
+

+ Internal Linking +

+
+
+ + {/* Content */} +
+ {/* Count */} +
+
+
+ {internalLinkingAnalysis?.totalLinks} +
+
+ Total Links +
+
+ +
+
+ {internalLinkingAnalysis?.uniqueLinks} +
+
+ Unique Links +
+
+
+ +
+
+ Recommendations +
+

{internalLinkingAnalysis?.recommendations}

+
+
+
+ {/* Image Analysis */}
{/* Heading */} @@ -935,7 +990,7 @@ function ResultsComponent() {
{/* Technical Analysis */} -
+ {/*

Technical Analysis

-
+
*/}
From e6f3edbf30dc89f2a1b4e727a9e6802d709c2bc3 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Tue, 23 Jul 2024 08:37:20 +0200 Subject: [PATCH 09/24] added meta description analysis --- wee/frontend/src/app/(pages)/results/page.tsx | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 906f1d7f9..1f82b5b46 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -19,7 +19,7 @@ import { InfoPopOver } from '../../components/InfoPopOver'; import jsPDF from 'jspdf'; import { saveReport } from '../../services/SaveReportService'; import { Metadata, ErrorResponse } from '../../models/ScraperModels'; -import { FiCheck, FiSearch, FiEye, FiSmartphone, FiClock, FiImage, FiAnchor, FiLink } from "react-icons/fi"; +import { FiCheck, FiSearch, FiEye, FiSmartphone, FiClock, FiImage, FiAnchor, FiLink, FiCode } from "react-icons/fi"; interface Classifications { label: string; @@ -63,6 +63,12 @@ interface InternalLinking { uniqueLinks: number; } +interface MetaDescription { + length: number; + recommendations: string; + titleTag: string; +} + export default function Results() { return ( Loading...
}> @@ -104,6 +110,7 @@ function ResultsComponent() { const [headingAnalysis, setHeadingAnalysis] = useState(); const [imagesAnalysis, setImageAnalysis] = useState(); const [internalLinkingAnalysis, setInternalLinkingAnalysis] = useState(); + const [metaDescriptionAnalysis, setMetaDescriptionAnalysis] = useState(); useEffect(() => { if (url) { @@ -143,6 +150,7 @@ function ResultsComponent() { setHeadingAnalysis(urlResults[0].seoAnalysis.headingAnalysis); setImageAnalysis(urlResults[0].seoAnalysis.imageAnalysis); setInternalLinkingAnalysis(urlResults[0].seoAnalysis.internalLinksAnalysis); + setMetaDescriptionAnalysis(urlResults[0].seoAnalysis.metaDescriptionAnalysis); } } } @@ -854,6 +862,45 @@ function ResultsComponent() {
+ {/* MetaDescription Analysis */} +
+ {/* Heading */} +
+
+ +
+
+

+ Meta Description +

+
+
+ + {/* Content */} +
+
+
+ Title Tag +
+

{metaDescriptionAnalysis?.titleTag}

+
+ +
+
+ Length +
+

{metaDescriptionAnalysis?.length}

+
+ +
+
+ Recommendations +
+

{metaDescriptionAnalysis?.recommendations}

+
+
+
+ {/* Image Analysis */}
{/* Heading */} From 6016669145165b44874a6536fc0784aebfe6fa5f Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Tue, 23 Jul 2024 10:44:56 +0200 Subject: [PATCH 10/24] Allow for error handling, states can be on type analysis or error, display only recommendation if there are --- wee/frontend/src/app/(pages)/results/page.tsx | 449 ++++++++++-------- 1 file changed, 241 insertions(+), 208 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 1f82b5b46..be3ac0d04 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -19,7 +19,8 @@ import { InfoPopOver } from '../../components/InfoPopOver'; import jsPDF from 'jspdf'; import { saveReport } from '../../services/SaveReportService'; import { Metadata, ErrorResponse } from '../../models/ScraperModels'; -import { FiCheck, FiSearch, FiEye, FiSmartphone, FiClock, FiImage, FiAnchor, FiLink, FiCode } from "react-icons/fi"; +import { FiSearch, FiImage, FiAnchor, FiLink, FiCode } from "react-icons/fi"; +import { TitleTagsAnalysis, HeadingAnalysis, ImageAnalysis, InternalLinksAnalysis, MetaDescriptionAnalysis, SEOError } from '../../models/ScraperModels'; interface Classifications { label: string; @@ -31,54 +32,36 @@ interface SummaryInfo { description: string; } -interface TitleTags { - length: number; - metaDescription: string; - recommendations: string; -} -interface Headings { - count: number; - headings: string[]; - recommendations: string; +export default function Results() { + return ( + Loading...
}> + + + ); } -interface Images { - errorUrls: string[]; - missingAltTextCount: number; - nonOptimizedCount: number; - reasonsMap: ReasonsMap; - recommendations: string; - totalImages: number; +function isMetadata(data: Metadata | ErrorResponse): data is Metadata { + return 'title' in data || 'ogTitle' in data || 'description' in data || 'ogDescription' in data; } -interface ReasonsMap { - format: string[]; - other: string[]; - size: string[]; +function isTitleTagAnalysis(data: TitleTagsAnalysis | SEOError): data is TitleTagsAnalysis { + return 'length' in data || 'metaDescription' in data || 'recommendations' in data; } -interface InternalLinking { - recommendations: string; - totalLinks: number; - uniqueLinks: number; +function isHeadingAnalysis(data: HeadingAnalysis | SEOError): data is HeadingAnalysis { + return 'count' in data || 'headings' in data || 'recommendations' in data; } -interface MetaDescription { - length: number; - recommendations: string; - titleTag: string; +function isImageAnalysis(data: ImageAnalysis | SEOError): data is ImageAnalysis { + return 'errorUrls' in data || 'missingAltTextCount' in data || 'nonOptimizedCount' in data || 'reasonsMap' in data || 'recommendations' in data || 'totalImages' in data ; } -export default function Results() { - return ( - Loading...
}> - - - ); +function isInternalLinkAnalysis(data: InternalLinksAnalysis | SEOError): data is InternalLinksAnalysis { + return 'recommendations' in data || 'totalLinks' in data || 'uniqueLinks' in data; } -function isMetadata(data: Metadata | ErrorResponse): data is Metadata { - return 'title' in data || 'ogTitle' in data || 'description' in data || 'ogDescription' in data; +function isMetaDescriptionAnalysis(data: MetaDescriptionAnalysis | SEOError): data is MetaDescriptionAnalysis { + return 'length' in data || 'recommendations' in data || 'titleTag' in data; } function ResultsComponent() { @@ -106,11 +89,11 @@ function ResultsComponent() { const [emails, setEmails] = useState([]); const [phones, setPhones] = useState([]); const [socialLinks, setSocialLinks] = useState([]); - const [titleTagsAnalysis, setTitleTagAnalysis] = useState(); - const [headingAnalysis, setHeadingAnalysis] = useState(); - const [imagesAnalysis, setImageAnalysis] = useState(); - const [internalLinkingAnalysis, setInternalLinkingAnalysis] = useState(); - const [metaDescriptionAnalysis, setMetaDescriptionAnalysis] = useState(); + const [titleTagsAnalysis, setTitleTagAnalysis] = useState(); + const [headingAnalysis, setHeadingAnalysis] = useState(); + const [imagesAnalysis, setImageAnalysis] = useState(); + const [internalLinkingAnalysis, setInternalLinkingAnalysis] = useState(); + const [metaDescriptionAnalysis, setMetaDescriptionAnalysis] = useState(); useEffect(() => { if (url) { @@ -788,33 +771,43 @@ function ResultsComponent() {
{/* Content */} -
-
-
- List of Headings -
-
    - {headingAnalysis?.headings.map((heading) => ( -
  • {heading}
  • - ))} -
-
+ { + headingAnalysis && isHeadingAnalysis(headingAnalysis) ? +
+
+
+ List of Headings +
+
    + {headingAnalysis?.headings.map((heading) => ( +
  • {heading}
  • + ))} +
+
-
-
- Count -
-

{headingAnalysis?.count}

-
+
+
+ Count +
+

{headingAnalysis?.count}

+
-
-
- Recommendations -
-

{headingAnalysis?.recommendations}

+ { + headingAnalysis?.recommendations != '' && +
+
+ Recommendations +
+

{headingAnalysis?.recommendations}

+
+ }
-
-
+ : + <> + {headingAnalysis?.error} + + } +
{/* EO Heading Analysis */} {/* Internal Linking Analysis */}
@@ -831,36 +824,46 @@ function ResultsComponent() {
{/* Content */} -
- {/* Count */} -
-
-
- {internalLinkingAnalysis?.totalLinks} -
-
- Total Links -
-
+ { + internalLinkingAnalysis && isInternalLinkAnalysis(internalLinkingAnalysis) ? +
+ {/* Count */} +
+
+
+ {internalLinkingAnalysis?.totalLinks} +
+
+ Total Links +
+
-
-
- {internalLinkingAnalysis?.uniqueLinks} -
-
- Unique Links +
+
+ {internalLinkingAnalysis?.uniqueLinks} +
+
+ Unique Links +
+
-
-
-
-
- Recommendations -
-

{internalLinkingAnalysis?.recommendations}

-
-
-
+ { + internalLinkingAnalysis?.recommendations != '' && +
+
+ Recommendations +
+

{internalLinkingAnalysis?.recommendations}

+
+ } +
+ : + <> + {internalLinkingAnalysis?.error} + + } +
{/* EO Internal Linking Analysis */} {/* MetaDescription Analysis */}
@@ -877,29 +880,39 @@ function ResultsComponent() {
{/* Content */} -
-
-
- Title Tag -
-

{metaDescriptionAnalysis?.titleTag}

-
+ { + metaDescriptionAnalysis && isMetaDescriptionAnalysis(metaDescriptionAnalysis) ? +
+
+
+ Title Tag +
+

{metaDescriptionAnalysis?.titleTag}

+
-
-
- Length -
-

{metaDescriptionAnalysis?.length}

-
+
+
+ Length +
+

{metaDescriptionAnalysis?.length}

+
-
-
- Recommendations -
-

{metaDescriptionAnalysis?.recommendations}

-
-
-
+ { + metaDescriptionAnalysis?.recommendations != '' && +
+
+ Recommendations +
+

{metaDescriptionAnalysis?.recommendations}

+
+ } +
+ : + <> + {metaDescriptionAnalysis?.error} + + } +
{/* EO MetaDescription Analysis */} {/* Image Analysis */}
@@ -916,84 +929,94 @@ function ResultsComponent() {
{/* Content */} -
- {/* Count */} -
-
-
- {imagesAnalysis?.totalImages} -
-
- Total Images -
-
+ { + imagesAnalysis && isImageAnalysis(imagesAnalysis) ? +
+ {/* Count */} +
+
+
+ {imagesAnalysis?.totalImages} +
+
+ Total Images +
+
-
-
- {imagesAnalysis?.missingAltTextCount} -
-
- Missing Alt. Text -
-
+
+
+ {imagesAnalysis?.missingAltTextCount} +
+
+ Missing Alt. Text +
+
-
-
- {imagesAnalysis?.nonOptimizedCount} +
+
+ {imagesAnalysis?.nonOptimizedCount} +
+
+ Non-Optimized Images +
+
-
- Non-Optimized Images + +
+
+ The format of the following URLs are incorrect +
+
+ {imagesAnalysis?.reasonsMap.format.map((formatUrl, index) => ( +

+ {formatUrl} +

+ ))} +
-
-
-
-
- The format of the following URLs are incorrect -
-
- {imagesAnalysis?.reasonsMap.format.map((formatUrl, index) => ( -

- {formatUrl} -

- ))} -
-
+
+
+ The size of the following URLs are to big +
+
+ {imagesAnalysis?.reasonsMap.size.map((reasonUrl, index) => ( +

+ {reasonUrl} +

+ ))} +
+
-
-
- The size of the following URLs are to big -
-
- {imagesAnalysis?.reasonsMap.size.map((reasonUrl, index) => ( -

- {reasonUrl} -

- ))} -
-
+
+
+ The following images have some other problems +
+
+ {imagesAnalysis?.reasonsMap.other.map((otherUrl, index) => ( +

+ {otherUrl} +

+ ))} +
+
-
-
- The following images have some other problems -
-
- {imagesAnalysis?.reasonsMap.other.map((otherUrl, index) => ( -

- {otherUrl} -

- ))} + { + imagesAnalysis?.recommendations != '' && +
+
+ Recommendations +
+

{imagesAnalysis?.recommendations}

+
+ }
-
- -
-
- Recommendations -
-

{imagesAnalysis?.recommendations}

-
-
-
+ : + <> + {imagesAnalysis?.error} + + } +
{/* EO Image Analysis */} {/* Title Tags */}
@@ -1008,33 +1031,43 @@ function ResultsComponent() {

- + {/* Content */} -
-
-
- Metadata Description -
-

{titleTagsAnalysis?.metaDescription}

-
- -
-
- Length -
-

{titleTagsAnalysis?.length}

-
+ { + titleTagsAnalysis && isTitleTagAnalysis(titleTagsAnalysis) ? +
+
+
+ Metadata Description +
+

{titleTagsAnalysis?.metaDescription}

+
-
-
- Recommendations -
-

{titleTagsAnalysis?.recommendations}

-
-
-
+
+
+ Length +
+

{titleTagsAnalysis?.length}

+
- + { + titleTagsAnalysis?.recommendations != '' && +
+
+ Recommendations +
+

{titleTagsAnalysis?.recommendations}

+
+ } + + : + <> + {titleTagsAnalysis?.error} + + } + {/* EO title tag */} + + {/* EO on page SEO analysis */} {/* Technical Analysis */} {/*
From 92ea517cebd03d676c25c6b96befb213fcee66bf Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Tue, 23 Jul 2024 10:58:24 +0200 Subject: [PATCH 11/24] fixed li error in the console --- wee/frontend/src/app/(pages)/results/page.tsx | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index be3ac0d04..28a3dde57 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -731,21 +731,8 @@ function ResultsComponent() { - {/* Keyword Analysis */} - {/*
-

- Keyword Analysis - -

-
*/} - {/* Onpage Analysis */}
- {/* Onpage Analysis Heading */}

On-Page Analysis @@ -779,8 +766,8 @@ function ResultsComponent() { List of Headings

    - {headingAnalysis?.headings.map((heading) => ( -
  • {heading}
  • + {headingAnalysis?.headings.map((heading, index) => ( +
  • {heading}
  • ))}
@@ -1068,18 +1055,6 @@ function ResultsComponent() {
{/* EO title tag */} {/* EO on page SEO analysis */} - - {/* Technical Analysis */} - {/*
-

- Technical Analysis - -

-
*/}
From 45b77140ce090f5c94a97baf4ad9c0fbf709df38 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Tue, 23 Jul 2024 16:23:44 +0200 Subject: [PATCH 12/24] only add urls in reason map if the array isn't empty --- wee/frontend/src/app/(pages)/results/page.tsx | 81 ++++++++++--------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 28a3dde57..13be4027e 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -949,44 +949,53 @@ function ResultsComponent() { -
-
- The format of the following URLs are incorrect -
-
- {imagesAnalysis?.reasonsMap.format.map((formatUrl, index) => ( -

- {formatUrl} -

- ))} -
-
+ { + imagesAnalysis?.reasonsMap.format.length != 0 && +
+
+ The format of the following URLs are incorrect +
+
+ {imagesAnalysis?.reasonsMap.format.map((formatUrl, index) => ( +

+ {formatUrl} +

+ ))} +
+
+ } -
-
- The size of the following URLs are to big -
-
- {imagesAnalysis?.reasonsMap.size.map((reasonUrl, index) => ( -

- {reasonUrl} -

- ))} -
-
+ { + imagesAnalysis?.reasonsMap.size.length != 0 && +
+
+ The size of the following URLs are to big +
+
+ {imagesAnalysis?.reasonsMap.size.map((reasonUrl, index) => ( +

+ {reasonUrl} +

+ ))} +
+
+ } -
-
- The following images have some other problems -
-
- {imagesAnalysis?.reasonsMap.other.map((otherUrl, index) => ( -

- {otherUrl} -

- ))} -
-
+ { + imagesAnalysis?.reasonsMap.other.length != 0 && +
+
+ The following images have some other problems +
+
+ {imagesAnalysis?.reasonsMap.other.map((otherUrl, index) => ( +

+ {otherUrl} +

+ ))} +
+
+ } { imagesAnalysis?.recommendations != '' && From 51ae5d0a55fbf1167b409718c429528050de3b21 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Wed, 24 Jul 2024 14:49:24 +0200 Subject: [PATCH 13/24] add custom tabs for ui --- wee/frontend/src/app/components/Util/Tabs.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 wee/frontend/src/app/components/Util/Tabs.tsx diff --git a/wee/frontend/src/app/components/Util/Tabs.tsx b/wee/frontend/src/app/components/Util/Tabs.tsx new file mode 100644 index 000000000..259b639b6 --- /dev/null +++ b/wee/frontend/src/app/components/Util/Tabs.tsx @@ -0,0 +1,20 @@ +import {extendVariants, Tab, Tabs} from "@nextui-org/react"; + +const WEETabs = extendVariants(Tabs, { + variants: { + color: { + stone: { + cursor: [ + "bg-jungleGreen-700", + // dark + "dark:bg-jungleGreen-400", + ], + } + } + }, + defaultVariants: { + color: "stone" + } +}) + +export default WEETabs; \ No newline at end of file From 9efccdd10f9411a67bae94cde6082d2493cebd89 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Wed, 24 Jul 2024 17:21:17 +0200 Subject: [PATCH 14/24] basic unique content ui and integration --- wee/frontend/src/app/(pages)/results/page.tsx | 68 ++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 13be4027e..f2fb574d7 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -19,8 +19,8 @@ import { InfoPopOver } from '../../components/InfoPopOver'; import jsPDF from 'jspdf'; import { saveReport } from '../../services/SaveReportService'; import { Metadata, ErrorResponse } from '../../models/ScraperModels'; -import { FiSearch, FiImage, FiAnchor, FiLink, FiCode } from "react-icons/fi"; -import { TitleTagsAnalysis, HeadingAnalysis, ImageAnalysis, InternalLinksAnalysis, MetaDescriptionAnalysis, SEOError } from '../../models/ScraperModels'; +import { FiSearch, FiImage, FiAnchor, FiLink, FiCode, FiUmbrella, FiBook } from "react-icons/fi"; +import { TitleTagsAnalysis, HeadingAnalysis, ImageAnalysis, InternalLinksAnalysis, MetaDescriptionAnalysis, UniqueContentAnalysis, SEOError } from '../../models/ScraperModels'; interface Classifications { label: string; @@ -64,6 +64,10 @@ function isMetaDescriptionAnalysis(data: MetaDescriptionAnalysis | SEOError): da return 'length' in data || 'recommendations' in data || 'titleTag' in data; } +function isUniqueContentAnalysis(data: UniqueContentAnalysis | SEOError): data is UniqueContentAnalysis { + return 'recommendations' in data || 'textLength' in data || 'uniqueWordsPercentage' in data || 'repeatedWords' in data; +} + function ResultsComponent() { const iconClasses = "text-xl text-default-500 pointer-events-none flex-shrink-0"; @@ -94,6 +98,7 @@ function ResultsComponent() { const [imagesAnalysis, setImageAnalysis] = useState(); const [internalLinkingAnalysis, setInternalLinkingAnalysis] = useState(); const [metaDescriptionAnalysis, setMetaDescriptionAnalysis] = useState(); + const [uniqContentAnalysis, setUniqueContentAnalysis] = useState(); useEffect(() => { if (url) { @@ -134,6 +139,7 @@ function ResultsComponent() { setImageAnalysis(urlResults[0].seoAnalysis.imageAnalysis); setInternalLinkingAnalysis(urlResults[0].seoAnalysis.internalLinksAnalysis); setMetaDescriptionAnalysis(urlResults[0].seoAnalysis.metaDescriptionAnalysis); + setUniqueContentAnalysis(urlResults[0].seoAnalysis.uniqueContentAnalysis); } } } @@ -748,7 +754,7 @@ function ResultsComponent() { {/* Heading */}
- +

@@ -1063,6 +1069,62 @@ function ResultsComponent() { }

{/* EO title tag */} + {/* Unique Content Analysis */} +
+ {/* Heading */} +
+
+ +
+
+

+ Unique Content +

+
+
+ + {/* Content */} + { + uniqContentAnalysis && isUniqueContentAnalysis(uniqContentAnalysis) ? +
+ {/* Count */} +
+
+
+ {uniqContentAnalysis?.textLength} +
+
+ Text Length +
+
+ +
+
+ {(uniqContentAnalysis?.uniqueWordsPercentage).toFixed(2)}% +
+
+ Unique words +
+
+
+ + { + uniqContentAnalysis?.recommendations != '' && +
+
+ Recommendations +
+

{uniqContentAnalysis?.recommendations}

+
+ } +
+ : + <> + {uniqContentAnalysis?.error} + + } +
{/* EO Unique Content Analysis */} +
{/* EO on page SEO analysis */} From 1b6aeae1fec16aa95152f3ab018c5847a9317d39 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Wed, 24 Jul 2024 17:50:32 +0200 Subject: [PATCH 15/24] added repeated words and the filtering --- wee/frontend/src/app/(pages)/results/page.tsx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index f2fb574d7..cb9aedb1b 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -79,6 +79,8 @@ function ResultsComponent() { const router = useRouter(); + const excludedUniqueRepeatedWords = ['for', 'in', 'to', 'a', 'to', 'the', 'with', 'on', 'and', 'you', 'your']; + const [websiteStatus, setWebsiteStatus] = useState(''); const [isCrawlable, setIsCrawlable] = useState(false); const [industryClassification, setIndustryClassification] = @@ -1108,6 +1110,28 @@ function ResultsComponent() { +
+
+ Repeated words +
+
+ {uniqContentAnalysis?.repeatedWords + .filter((wordObj) => !excludedUniqueRepeatedWords.includes(wordObj.word)) + .map((wordObj, index) => ( + + + {wordObj.word}: {wordObj.count} + + + ))} +
+
+ { uniqContentAnalysis?.recommendations != '' &&
From 135b87347246f4097553e9daa88de2203170a971 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Wed, 24 Jul 2024 19:57:13 +0200 Subject: [PATCH 16/24] added isURLinDescription --- wee/frontend/src/app/(pages)/results/page.tsx | 16 +++++++++++----- wee/frontend/src/app/models/ScraperModels.ts | 1 + .../src/seo-analysis/seo-analysis.service.ts | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index cb9aedb1b..661371445 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -45,7 +45,7 @@ function isMetadata(data: Metadata | ErrorResponse): data is Metadata { } function isTitleTagAnalysis(data: TitleTagsAnalysis | SEOError): data is TitleTagsAnalysis { - return 'length' in data || 'metaDescription' in data || 'recommendations' in data; + return 'length' in data || 'metaDescription' in data || 'recommendations' in data || 'isUrlWordsInDescription' in data; } function isHeadingAnalysis(data: HeadingAnalysis | SEOError): data is HeadingAnalysis { @@ -79,7 +79,7 @@ function ResultsComponent() { const router = useRouter(); - const excludedUniqueRepeatedWords = ['for', 'in', 'to', 'a', 'to', 'the', 'with', 'on', 'and', 'you', 'your']; + const excludedUniqueRepeatedWords = ['for', 'in', 'to', 'a', 'the', 'with', 'on', 'and', 'you', 'your', 'of', 'is', 'r']; const [websiteStatus, setWebsiteStatus] = useState(''); const [isCrawlable, setIsCrawlable] = useState(false); @@ -1054,6 +1054,13 @@ function ResultsComponent() {

{titleTagsAnalysis?.length}

+
+
+ Is URL in description? +
+

{titleTagsAnalysis?.isUrlWordsInDescription == true ? 'Yes' : 'No'}

+
+ { titleTagsAnalysis?.recommendations != '' &&
@@ -1118,10 +1125,9 @@ function ResultsComponent() { {uniqContentAnalysis?.repeatedWords .filter((wordObj) => !excludedUniqueRepeatedWords.includes(wordObj.word)) .map((wordObj, index) => ( - + diff --git a/wee/frontend/src/app/models/ScraperModels.ts b/wee/frontend/src/app/models/ScraperModels.ts index 9021c38f8..b676e819b 100644 --- a/wee/frontend/src/app/models/ScraperModels.ts +++ b/wee/frontend/src/app/models/ScraperModels.ts @@ -132,6 +132,7 @@ export interface StructuredDataAnalysis { } export interface TitleTagsAnalysis { + isUrlWordsInDescription: boolean; length: number; metaDescription: string; recommendations: string; diff --git a/wee/webscraper/src/seo-analysis/seo-analysis.service.ts b/wee/webscraper/src/seo-analysis/seo-analysis.service.ts index 760a28dcc..254cf6663 100644 --- a/wee/webscraper/src/seo-analysis/seo-analysis.service.ts +++ b/wee/webscraper/src/seo-analysis/seo-analysis.service.ts @@ -83,7 +83,7 @@ export class SeoAnalysisService { metaDescription, length, // isOptimized, - // isUrlWordsInDescription, + isUrlWordsInDescription, recommendations: recommendations.trim(), }; } From fe7bd9ee964470dad0f557dcf666c423c80e82cf Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Wed, 24 Jul 2024 20:58:04 +0200 Subject: [PATCH 17/24] UnitTest: Extended mockResult obj so that existing tests pass for Result page --- wee/frontend/specs/unit/Results.spec.tsx | 51 ++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/wee/frontend/specs/unit/Results.spec.tsx b/wee/frontend/specs/unit/Results.spec.tsx index c829f8f3f..753943d47 100644 --- a/wee/frontend/specs/unit/Results.spec.tsx +++ b/wee/frontend/specs/unit/Results.spec.tsx @@ -83,6 +83,57 @@ describe('Results Component', () => { "https://twitter.com/AbsaSouthAfrica", "https://www.linkedin.com/company/absa/" ] + }, + seoAnalysis: { + // XMLSitemapAnalysis: {}, + // canonicalTagAnalysis: {}, + headingAnalysis: { + count: 2, + headings: ['HeadingOne', 'HeadingTwo'], + recommendations: '', + }, + imageAnalysis: { + errorUrls: [], + missingAltTextCount: 2, + nonOptimizedCount: 8, + reasonsMap: { + format: [], + other: [], + size: [], + }, + recommendations: '', + totalImages: 20, + }, + // indexabilityAnalysis: {}, + internalLinksAnalysis: { + recommendations: '', + totalLinks: 0, + uniqueLinks: 0, + }, + metaDescriptionAnalysis: { + length: 2, + recommendations: '', + titleTag: 'myTitleTag', + }, + // mobileFriendlinessAnalysis: {}, + // structuredDataAnalysis: {}, + titleTagsAnalysis: { + isUrlWordsInDescription: false, + length: 183, + metaDescription: '', + recommendations: '', + }, + uniqueContentAnalysis: { + recommendations: '', + textLength: 12, + uniqueWordsPercentage: 16, + repeatedWords: [ + { + word: 'and', + count: 12, + } + ] + }, } }, ]; From c5504e176f797d88fc10ce5dead96ee5f8dde16f Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Wed, 24 Jul 2024 21:52:03 +0200 Subject: [PATCH 18/24] UnitTesting: added unit testing for Internal Linking and Meta Description --- wee/frontend/specs/unit/Results.spec.tsx | 106 +++++++++++++++++- wee/frontend/src/app/(pages)/results/page.tsx | 8 +- 2 files changed, 104 insertions(+), 10 deletions(-) diff --git a/wee/frontend/specs/unit/Results.spec.tsx b/wee/frontend/specs/unit/Results.spec.tsx index 753943d47..82107606b 100644 --- a/wee/frontend/specs/unit/Results.spec.tsx +++ b/wee/frontend/specs/unit/Results.spec.tsx @@ -106,14 +106,14 @@ describe('Results Component', () => { }, // indexabilityAnalysis: {}, internalLinksAnalysis: { - recommendations: '', - totalLinks: 0, - uniqueLinks: 0, + recommendations: 'This is the internal linking recommendation', + totalLinks: 17, + uniqueLinks: 9, }, metaDescriptionAnalysis: { - length: 2, - recommendations: '', - titleTag: 'myTitleTag', + length: 80, + recommendations: "Title tag length should be between 50 and 60 characters.", + titleTag: "South African Online Computer Store", }, // mobileFriendlinessAnalysis: {}, // structuredDataAnalysis: {}, @@ -439,6 +439,100 @@ describe('Results Component', () => { }); }); + it('Onpage SEO: Internal Linking - display total, unique links and recommendation', async () => { + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.totalLinks)).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.uniqueLinks)).toBeDefined(); + expect(screen.queryByTestId('internalLinking_recommendations')).toBeInTheDocument(); + expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.recommendations)).toBeDefined(); + }); + }); + + it('Onpage SEO: Internal Linking - display total, unique links and NO recommendation', async () => { + (useScrapingContext as jest.Mock).mockReturnValueOnce({ + results: [ + { + ...mockResults[0], + seoAnalysis: { + ...mockResults[0].seoAnalysis, + internalLinksAnalysis: { + recommendations: '', + totalLinks: 17, + uniqueLinks: 9, + }, + } + }, + ], + }); + + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.totalLinks)).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.uniqueLinks)).toBeDefined(); + expect(screen.queryByTestId('internalLinking_recommendations')).not.toBeInTheDocument(); + }); + }); + + it('Onpage SEO: Meta Description - display title tag, length and recommendation', async () => { + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.titleTag)).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.length)).toBeDefined(); + expect(screen.queryByTestId('meta_recommendations')).toBeInTheDocument(); + expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.recommendations)).toBeDefined(); + }); + }); + + it('Onpage SEO: Meta Description - display title tag, length and NO recommendation', async () => { + (useScrapingContext as jest.Mock).mockReturnValueOnce({ + results: [ + { + ...mockResults[0], + seoAnalysis: { + ...mockResults[0].seoAnalysis, + metaDescriptionAnalysis: { + length: 55, + recommendations: '', + titleTag: "South African Online Computer Store", + }, + } + }, + ], + }); + + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.titleTag)).toBeDefined(); + expect(screen.getByText('55')).toBeDefined(); + expect(screen.queryByTestId('meta_recommendations')).not.toBeInTheDocument(); + }); + }); + it('should call jsPDF and download the PDF when download button is clicked', async () => { render(); diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 661371445..e7a0a7ed2 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -845,7 +845,7 @@ function ResultsComponent() { { internalLinkingAnalysis?.recommendations != '' && -
+
Recommendations
@@ -893,14 +893,14 @@ function ResultsComponent() {
{ - metaDescriptionAnalysis?.recommendations != '' && -
+ metaDescriptionAnalysis?.recommendations !== '' && ( +
Recommendations

{metaDescriptionAnalysis?.recommendations}

- } + )}
: <> From 8d62c13531714044ec04d9fecbbc8781b000e6e2 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Wed, 24 Jul 2024 22:46:18 +0200 Subject: [PATCH 19/24] UnitTesting: testing for images and title tags --- wee/frontend/specs/unit/Results.spec.tsx | 121 ++++++++++++++++-- wee/frontend/src/app/(pages)/results/page.tsx | 4 +- 2 files changed, 115 insertions(+), 10 deletions(-) diff --git a/wee/frontend/specs/unit/Results.spec.tsx b/wee/frontend/specs/unit/Results.spec.tsx index 82107606b..429fc0ee5 100644 --- a/wee/frontend/specs/unit/Results.spec.tsx +++ b/wee/frontend/specs/unit/Results.spec.tsx @@ -94,15 +94,15 @@ describe('Results Component', () => { }, imageAnalysis: { errorUrls: [], - missingAltTextCount: 2, - nonOptimizedCount: 8, + missingAltTextCount: 11, + nonOptimizedCount: 3, reasonsMap: { - format: [], + format: ['https://www.exampleOne.com/coast.png','https://www.exampleTwo.com/lion.svg','https://www.exampleThree.com/ocean.jpg'], other: [], size: [], }, - recommendations: '', - totalImages: 20, + recommendations: '11 images are missing alt text. 3 images are not optimized.', + totalImages: 27, }, // indexabilityAnalysis: {}, internalLinksAnalysis: { @@ -119,9 +119,9 @@ describe('Results Component', () => { // structuredDataAnalysis: {}, titleTagsAnalysis: { isUrlWordsInDescription: false, - length: 183, - metaDescription: '', - recommendations: '', + length: 88, + metaDescription: "Buy computers, hardware, software, laptops & more from South Africa's best online store.", + recommendations: 'Meta description length should be between 120 and 160 characters. Consider including words from the URL in the meta description: wootware.', }, uniqueContentAnalysis: { recommendations: '', @@ -533,6 +533,111 @@ describe('Results Component', () => { }); }); + it('Onpage SEO: Images - display total images, missing alt text, non-optimised images, list of urls which format is incorrent and recommendation', async () => { + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.totalImages)).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.missingAltTextCount)).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.nonOptimizedCount)).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.reasonsMap.format[0])).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.reasonsMap.format[2])).toBeDefined(); + expect(screen.queryByTestId('images_recommendations')).toBeInTheDocument(); + expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.recommendations)).toBeDefined(); + }); + }); + + it('Onpage SEO: Images - display total images, missing alt text, non-optimised images and NO recommendation', async () => { + (useScrapingContext as jest.Mock).mockReturnValueOnce({ + results: [ + { + ...mockResults[0], + seoAnalysis: { + ...mockResults[0].seoAnalysis, + imageAnalysis: { + errorUrls: [], + missingAltTextCount: 6, + nonOptimizedCount: 0, + reasonsMap: { + format: [], + other: [], + size: [], + }, + recommendations: '', + totalImages: 34, + }, + } + }, + ], + }); + + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.queryByTestId('images_recommendations')).not.toBeInTheDocument(); + }); + }); + + it('Onpage SEO: Title Tags - metadata description, length, is url in description and recommendation', async () => { + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.metaDescription)).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.length)).toBeDefined(); + expect(screen.getByText('No')).toBeDefined(); + expect(screen.queryByTestId('titleTag_recommendations')).toBeInTheDocument(); + expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.recommendations)).toBeDefined(); + }); + }); + + it('Onpage SEO: Title Tags - metadata description, length, is url in description and NO recommendation', async () => { + (useScrapingContext as jest.Mock).mockReturnValueOnce({ + results: [ + { + ...mockResults[0], + seoAnalysis: { + ...mockResults[0].seoAnalysis, + titleTagsAnalysis: { + isUrlWordsInDescription: true, + length: 121, + metaDescription: "Meta description for title tag analysis", + recommendations: '', + }, + } + }, + ], + }); + + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText("Meta description for title tag analysis")).toBeDefined(); + expect(screen.getByText("121")).toBeDefined(); + expect(screen.getByText('Yes')).toBeDefined(); + expect(screen.queryByTestId('titleTag_recommendations')).not.toBeInTheDocument(); + }); + }); + it('should call jsPDF and download the PDF when download button is clicked', async () => { render(); diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index e7a0a7ed2..584c36f47 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -1007,7 +1007,7 @@ function ResultsComponent() { { imagesAnalysis?.recommendations != '' && -
+
Recommendations
@@ -1063,7 +1063,7 @@ function ResultsComponent() { { titleTagsAnalysis?.recommendations != '' && -
+
Recommendations
From 3d033628fdf7d0375d7fd9e4911e0e56d34796d6 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Thu, 25 Jul 2024 12:00:59 +0200 Subject: [PATCH 20/24] UnitTesting: Added unit content tests --- wee/frontend/specs/unit/Results.spec.tsx | 267 ++++++++++++------ wee/frontend/src/app/(pages)/results/page.tsx | 4 +- 2 files changed, 180 insertions(+), 91 deletions(-) diff --git a/wee/frontend/specs/unit/Results.spec.tsx b/wee/frontend/specs/unit/Results.spec.tsx index 429fc0ee5..845de55cf 100644 --- a/wee/frontend/specs/unit/Results.spec.tsx +++ b/wee/frontend/specs/unit/Results.spec.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { render, screen, act, waitFor, fireEvent } from '@testing-library/react'; -import Results from '../../src/app/(pages)/results/page'; +import Results from '../../src/app/(pages)/results/page'; import { useSearchParams } from 'next/navigation'; import { useRouter } from 'next/navigation'; import { useUserContext } from '../../src/app/context/UserContext'; import { useScrapingContext } from '../../src/app/context/ScrapingContext' -import jsPDF from 'jspdf'; +import jsPDF from 'jspdf'; import { saveReport } from '../../src/app/services/SaveReportService'; import '@testing-library/jest-dom'; import exp from 'constants'; @@ -19,24 +19,24 @@ jest.mock('next/navigation', () => ({ jest.mock('jspdf', () => ({ __esModule: true, default: jest.fn().mockImplementation(() => ({ - setFontSize: jest.fn(), - text: jest.fn(), - rect: jest.fn(), - setFillColor: jest.fn(), - setTextColor: jest.fn(), - setDrawColor: jest.fn(), - line: jest.fn(), - addPage: jest.fn(), - save: jest.fn(), - getStringUnitWidth: jest.fn().mockReturnValue(50), - internal: { - scaleFactor: 1.5, - pageSize: { width: 180, height: 297 }, - }, + setFontSize: jest.fn(), + text: jest.fn(), + rect: jest.fn(), + setFillColor: jest.fn(), + setTextColor: jest.fn(), + setDrawColor: jest.fn(), + line: jest.fn(), + addPage: jest.fn(), + save: jest.fn(), + getStringUnitWidth: jest.fn().mockReturnValue(50), + internal: { + scaleFactor: 1.5, + pageSize: { width: 180, height: 297 }, + }, })), })); - + jest.mock('../../src/app/context/ScrapingContext', () => ({ useScrapingContext: jest.fn(), })); @@ -97,7 +97,7 @@ describe('Results Component', () => { missingAltTextCount: 11, nonOptimizedCount: 3, reasonsMap: { - format: ['https://www.exampleOne.com/coast.png','https://www.exampleTwo.com/lion.svg','https://www.exampleThree.com/ocean.jpg'], + format: ['https://www.exampleOne.com/coast.png', 'https://www.exampleTwo.com/lion.svg', 'https://www.exampleThree.com/ocean.jpg'], other: [], size: [], }, @@ -125,15 +125,15 @@ describe('Results Component', () => { }, uniqueContentAnalysis: { recommendations: '', - textLength: 12, - uniqueWordsPercentage: 16, + textLength: 743, + uniqueWordsPercentage: 41.72, repeatedWords: [ { - word: 'and', - count: 12, + word: 'repeatedWordsOne', + count: 19, } ] - }, + }, } }, ]; @@ -144,9 +144,9 @@ describe('Results Component', () => { }; beforeEach(() => { - (useSearchParams as jest.Mock).mockReturnValue(new URLSearchParams(`url=${mockUrl}`)); - (useRouter as jest.Mock).mockReturnValue({ push: mockPush, back: mockBack}); - (useScrapingContext as jest.Mock).mockReturnValue({ results: mockResults }); + (useSearchParams as jest.Mock).mockReturnValue(new URLSearchParams(`url=${mockUrl}`)); + (useRouter as jest.Mock).mockReturnValue({ push: mockPush, back: mockBack }); + (useScrapingContext as jest.Mock).mockReturnValue({ results: mockResults }); (useUserContext as jest.Mock).mockReturnValue({ user: mockUser }); }); @@ -341,19 +341,19 @@ describe('Results Component', () => { it('should display the screenshot when it is present', async () => { (useScrapingContext as jest.Mock).mockReturnValueOnce({ - results: mockResults, + results: mockResults, }); - + await act(async () => { - render(); + render(); }); const mediaTab = screen.getByRole('tab', { name: /Media/i }); fireEvent.click(mediaTab); - + await waitFor(() => { - expect(screen.getByAltText('HomePageScreenShot')).toBeInTheDocument(); - expect(screen.getByAltText('HomePageScreenShot').src).toBe(`data:image/png;base64,${mockResults[0].screenshot}`); + expect(screen.getByAltText('HomePageScreenShot')).toBeInTheDocument(); + expect(screen.getByAltText('HomePageScreenShot').src).toBe(`data:image/png;base64,${mockResults[0].screenshot}`); }); }); @@ -377,7 +377,7 @@ describe('Results Component', () => { results: [ { ...mockResults[0], - robots: { errorStatus: 404, errorCode: 'Not Found', errorMessage: 'Page not found'}, + robots: { errorStatus: 404, errorCode: 'Not Found', errorMessage: 'Page not found' }, }, ], }); @@ -390,7 +390,7 @@ describe('Results Component', () => { expect(screen.queryByText('No')).toBeDefined(); }); }); - + it('should display correct summary information', async () => { await act(async () => { render(); @@ -450,7 +450,7 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.totalLinks)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.uniqueLinks)).toBeDefined(); - expect(screen.queryByTestId('internalLinking_recommendations')).toBeInTheDocument(); + expect(screen.queryByTestId('internalLinking_recommendations')).toBeInTheDocument(); expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.recommendations)).toBeDefined(); }); }); @@ -482,7 +482,7 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.totalLinks)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.uniqueLinks)).toBeDefined(); - expect(screen.queryByTestId('internalLinking_recommendations')).not.toBeInTheDocument(); + expect(screen.queryByTestId('internalLinking_recommendations')).not.toBeInTheDocument(); }); }); @@ -497,7 +497,7 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.titleTag)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.length)).toBeDefined(); - expect(screen.queryByTestId('meta_recommendations')).toBeInTheDocument(); + expect(screen.queryByTestId('meta_recommendations')).toBeInTheDocument(); expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.recommendations)).toBeDefined(); }); }); @@ -529,7 +529,7 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.titleTag)).toBeDefined(); expect(screen.getByText('55')).toBeDefined(); - expect(screen.queryByTestId('meta_recommendations')).not.toBeInTheDocument(); + expect(screen.queryByTestId('meta_recommendations')).not.toBeInTheDocument(); }); }); @@ -547,7 +547,7 @@ describe('Results Component', () => { expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.nonOptimizedCount)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.reasonsMap.format[0])).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.reasonsMap.format[2])).toBeDefined(); - expect(screen.queryByTestId('images_recommendations')).toBeInTheDocument(); + expect(screen.queryByTestId('images_recommendations')).toBeInTheDocument(); expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.recommendations)).toBeDefined(); }); }); @@ -584,7 +584,7 @@ describe('Results Component', () => { fireEvent.click(SEOTab); await waitFor(() => { - expect(screen.queryByTestId('images_recommendations')).not.toBeInTheDocument(); + expect(screen.queryByTestId('images_recommendations')).not.toBeInTheDocument(); }); }); @@ -600,7 +600,7 @@ describe('Results Component', () => { expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.metaDescription)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.length)).toBeDefined(); expect(screen.getByText('No')).toBeDefined(); - expect(screen.queryByTestId('titleTag_recommendations')).toBeInTheDocument(); + expect(screen.queryByTestId('titleTag_recommendations')).toBeInTheDocument(); expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.recommendations)).toBeDefined(); }); }); @@ -634,51 +634,141 @@ describe('Results Component', () => { expect(screen.getByText("Meta description for title tag analysis")).toBeDefined(); expect(screen.getByText("121")).toBeDefined(); expect(screen.getByText('Yes')).toBeDefined(); - expect(screen.queryByTestId('titleTag_recommendations')).not.toBeInTheDocument(); + expect(screen.queryByTestId('titleTag_recommendations')).not.toBeInTheDocument(); + }); + }); + + it('Onpage SEO: Unique Content - Text Length, Unique words, repeated words and NO recommendation', async () => { + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.uniqueContentAnalysis.textLength)).toBeDefined(); + expect(screen.getByText('41.72%')).toBeDefined(); + expect(screen.getByText('repeatedWordsOne: 19')).toBeDefined(); + expect(screen.queryByTestId('uniqueContent_recommendations')).not.toBeInTheDocument(); + }); + }); + + it('Onpage SEO: Unique Content - Text Length, Unique words, repeated words and recommendation', async () => { + (useScrapingContext as jest.Mock).mockReturnValueOnce({ + results: [ + { + ...mockResults[0], + seoAnalysis: { + ...mockResults[0].seoAnalysis, + uniqueContentAnalysis: { + recommendations: 'Content length should ideally be more than 500 characters.', + textLength: 743, + uniqueWordsPercentage: 41.72, + repeatedWords: [ + { + word: 'repeatedWordsOne', + count: 19, + } + ] + }, + } + }, + ], + }); + + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.uniqueContentAnalysis.textLength)).toBeDefined(); + expect(screen.getByText('41.72%')).toBeDefined(); + expect(screen.getByText('repeatedWordsOne: 19')).toBeDefined(); + + expect(screen.queryByTestId('uniqueContent_recommendations')).toBeInTheDocument(); + expect(screen.getByText('Content length should ideally be more than 500 characters.')).toBeDefined(); + }); + }); + + it('Onpage SEO: Unique Content - Text Length, Unique words, NO repeated words and recommendation', async () => { + (useScrapingContext as jest.Mock).mockReturnValueOnce({ + results: [ + { + ...mockResults[0], + seoAnalysis: { + ...mockResults[0].seoAnalysis, + uniqueContentAnalysis: { + recommendations: 'Content length should ideally be more than 500 characters.', + textLength: 743, + uniqueWordsPercentage: 41.72, + repeatedWords: [] + }, + } + }, + ], + }); + + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.uniqueContentAnalysis.textLength)).toBeDefined(); + expect(screen.getByText('41.72%')).toBeDefined(); + expect(screen.queryByText('repeatedWordsOne: 19')).not.toBeInTheDocument(); + expect(screen.queryByTestId('uniqueContent_recommendations')).toBeInTheDocument(); + expect(screen.getByText('Content length should ideally be more than 500 characters.')).toBeDefined(); }); }); it('should call jsPDF and download the PDF when download button is clicked', async () => { render(); - + // Ensure the component has rendered and the dropdown button is available const dropdownButton = screen.getByRole('button', { name: /export\/save/i }); expect(dropdownButton).toBeInTheDocument(); - + // Click the dropdown button to open the menu fireEvent.click(dropdownButton); - + // Wait for the download button to appear const downloadButton = await screen.findByTestId('download-report-button'); expect(downloadButton).toBeInTheDocument(); - + // Click the download button fireEvent.click(downloadButton); - + await waitFor(() => { - // Ensure jsPDF was called - expect(jsPDF).toHaveBeenCalled(); - + // Ensure jsPDF was called + expect(jsPDF).toHaveBeenCalled(); + }); }); it('should display a popup when the save button is clicked', async () => { render(); - + // Ensure the component has rendered and the dropdown button is available const dropdownButton = screen.getByRole('button', { name: /export\/save/i }); expect(dropdownButton).toBeInTheDocument(); - + // Click the dropdown button to open the menu fireEvent.click(dropdownButton); - + // Wait for the save button to appear const saveButton = await screen.findByTestId('save-report-button'); expect(saveButton).toBeInTheDocument(); - + // Click the save button fireEvent.click(saveButton); - + // wait for popup to appear const modal = await screen.findByTestId('save-report-modal'); expect(modal).toBeInTheDocument(); @@ -686,68 +776,68 @@ describe('Results Component', () => { it('should enter an error state if no report name is entered and save is clicked', async () => { render(); - + // Ensure the component has rendered and the dropdown button is available const dropdownButton = screen.getByRole('button', { name: /export\/save/i }); expect(dropdownButton).toBeInTheDocument(); - + // Click the dropdown button to open the menu fireEvent.click(dropdownButton); - + // Wait for the save button to appear const saveButton = await screen.findByTestId('save-report-button'); expect(saveButton).toBeInTheDocument(); - + // Click the save button fireEvent.click(saveButton); - + // wait for popup to appear const modal = await screen.findByTestId('save-report-modal'); expect(modal).toBeInTheDocument(); - + // Click the save button in the modal const saveModalButton = screen.getByRole('button', { name: /Save/i }); expect(saveModalButton).toBeInTheDocument(); fireEvent.click(saveModalButton); - + await waitFor(() => { - expect(saveReport).not.toHaveBeenCalled(); + expect(saveReport).not.toHaveBeenCalled(); }); // Ensure the error state is displayed in the Input component - const inputWithError = screen.getByLabelText('Report Name', { invalid: true, disabled: true}); + const inputWithError = screen.getByLabelText('Report Name', { invalid: true, disabled: true }); expect(inputWithError).toBeInTheDocument(); }); it('should enter an error state if name is entered then removed', async () => { render(); - + // Ensure the component has rendered and the dropdown button is available const dropdownButton = screen.getByRole('button', { name: /export\/save/i }); expect(dropdownButton).toBeInTheDocument(); - + // Click the dropdown button to open the menu fireEvent.click(dropdownButton); - + // Wait for the save button to appear const saveButton = await screen.findByTestId('save-report-button'); expect(saveButton).toBeInTheDocument(); - + // Click the save button fireEvent.click(saveButton); - + // Wait for the modal to appear const modal = await screen.findByTestId('save-report-modal'); expect(modal).toBeInTheDocument(); - + // Enter a report name const reportNameInput = screen.getByLabelText(/Report Name/i); expect(reportNameInput).toBeInTheDocument(); fireEvent.change(reportNameInput, { target: { value: 'Test Report' } }); - + // Clear the report name fireEvent.change(reportNameInput, { target: { value: '' } }); - + // Check if isInvalid and isDisabled are set to true expect(screen.getByLabelText('Report Name')).toHaveAttribute('aria-invalid', 'true'); @@ -758,21 +848,21 @@ describe('Results Component', () => { it('should call the saveReport function when the save button is clicked', async () => { render(); - + // Ensure the component has rendered and the dropdown button is available const dropdownButton = screen.getByRole('button', { name: /export\/save/i }); expect(dropdownButton).toBeInTheDocument(); - + // Click the dropdown button to open the menu fireEvent.click(dropdownButton); - + // Wait for the save button to appear const saveButton = await screen.findByTestId('save-report-button'); expect(saveButton).toBeInTheDocument(); - + // Click the save button fireEvent.click(saveButton); - + // wait for popup to appear const modal = await screen.findByTestId('save-report-modal'); expect(modal).toBeInTheDocument(); @@ -781,34 +871,34 @@ describe('Results Component', () => { const reportNameInput = screen.getByLabelText(/Report Name/i); expect(reportNameInput).toBeInTheDocument(); fireEvent.change(reportNameInput, { target: { value: 'Test Report' } }); - + // Click the save button in the modal const saveModalButton = screen.getByRole('button', { name: /Save/i }); expect(saveModalButton).toBeInTheDocument(); fireEvent.click(saveModalButton); - + await waitFor(() => { - expect(saveReport).toHaveBeenCalled(); + expect(saveReport).toHaveBeenCalled(); }); }); it('should display a success message when the report is saved successfully', async () => { render(); - + // Ensure the component has rendered and the dropdown button is available const dropdownButton = screen.getByRole('button', { name: /export\/save/i }); expect(dropdownButton).toBeInTheDocument(); - + // Click the dropdown button to open the menu fireEvent.click(dropdownButton); - + // Wait for the save button to appear const saveButton = await screen.findByTestId('save-report-button'); expect(saveButton).toBeInTheDocument(); - + // Click the save button fireEvent.click(saveButton); - + // wait for popup to appear const modal = await screen.findByTestId('save-report-modal'); expect(modal).toBeInTheDocument(); @@ -817,15 +907,14 @@ describe('Results Component', () => { const reportNameInput = screen.getByLabelText(/Report Name/i); expect(reportNameInput).toBeInTheDocument(); fireEvent.change(reportNameInput, { target: { value: 'Test Report' } }); - + // Click the save button in the modal const saveModalButton = screen.getByRole('button', { name: /Save/i }); expect(saveModalButton).toBeInTheDocument(); fireEvent.click(saveModalButton); - + await waitFor(() => { - expect(screen.getByText('Report saved successfully')).toBeDefined(); + expect(screen.getByText('Report saved successfully')).toBeDefined(); }); }); }); - \ No newline at end of file diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index 584c36f47..efb8cddc7 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -1063,7 +1063,7 @@ function ResultsComponent() { { titleTagsAnalysis?.recommendations != '' && -
+
Recommendations
@@ -1140,7 +1140,7 @@ function ResultsComponent() { { uniqContentAnalysis?.recommendations != '' && -
+
Recommendations
From e9cdc4814f1c74304534d143fa444c40cd65b678 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Thu, 25 Jul 2024 12:43:08 +0200 Subject: [PATCH 21/24] UnitTesting: added testing for headings --- wee/frontend/specs/unit/Results.spec.tsx | 84 ++++++++++++++++++- wee/frontend/src/app/(pages)/results/page.tsx | 2 +- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/wee/frontend/specs/unit/Results.spec.tsx b/wee/frontend/specs/unit/Results.spec.tsx index 845de55cf..ad4d8ed54 100644 --- a/wee/frontend/specs/unit/Results.spec.tsx +++ b/wee/frontend/specs/unit/Results.spec.tsx @@ -90,7 +90,7 @@ describe('Results Component', () => { headingAnalysis: { count: 2, headings: ['HeadingOne', 'HeadingTwo'], - recommendations: '', + recommendations: 'This is a heading recommendation', }, imageAnalysis: { errorUrls: [], @@ -728,6 +728,88 @@ describe('Results Component', () => { }); }); + it('Onpage SEO: Headings - display list of headings, heading count and recommendation', async () => { + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.count)).toBeDefined(); + expect(screen.queryByTestId('headings_recommendations')).toBeInTheDocument(); + expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.recommendations)).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.headings[0])).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.headings[1])).toBeDefined(); + }); + }); + + it('Onpage SEO: Headings - display list of headings, heading count and NO recommendation', async () => { + (useScrapingContext as jest.Mock).mockReturnValueOnce({ + results: [ + { + ...mockResults[0], + seoAnalysis: { + ...mockResults[0].seoAnalysis, + headingAnalysis: { + count: 2, + headings: ['HeadingOne', 'HeadingTwo'], + recommendations: '', + }, + } + }, + ], + }); + + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.count)).toBeDefined(); + expect(screen.queryByTestId('headings_recommendations')).not.toBeInTheDocument(); + expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.headings[0])).toBeDefined(); + expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.headings[1])).toBeDefined(); + }); + }); + + it('Onpage SEO: Headings - display NO list of headings, heading count and recommendation', async () => { + (useScrapingContext as jest.Mock).mockReturnValueOnce({ + results: [ + { + ...mockResults[0], + seoAnalysis: { + ...mockResults[0].seoAnalysis, + headingAnalysis: { + count: 0, + headings: [], + recommendations: 'This is a heading recommendation', + }, + } + }, + ], + }); + + await act(async () => { + render(); + }); + + const SEOTab = screen.getByRole('tab', { name: /SEO Analysis/i }); + fireEvent.click(SEOTab); + + await waitFor(() => { + expect(screen.getByText('0')).toBeDefined(); + expect(screen.queryByTestId('headings_recommendations')).toBeInTheDocument(); + expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.recommendations)).toBeDefined(); + expect(screen.queryByText(mockResults[0].seoAnalysis.headingAnalysis.headings[0])).not.toBeInTheDocument(); + expect(screen.queryByText(mockResults[0].seoAnalysis.headingAnalysis.headings[1])).not.toBeInTheDocument(); + }); + }); + it('should call jsPDF and download the PDF when download button is clicked', async () => { render(); diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index efb8cddc7..d41fd862e 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -789,7 +789,7 @@ function ResultsComponent() { { headingAnalysis?.recommendations != '' && -
+
Recommendations
From 098fea371a01f2a94757f7017bbb45a4e328b297 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Thu, 25 Jul 2024 13:06:44 +0200 Subject: [PATCH 22/24] fixed linting error --- wee/frontend/src/app/(pages)/results/page.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index d41fd862e..ca46e2790 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -1109,7 +1109,12 @@ function ResultsComponent() {
- {(uniqContentAnalysis?.uniqueWordsPercentage).toFixed(2)}% + {uniqContentAnalysis && uniqContentAnalysis.uniqueWordsPercentage + ? + (uniqContentAnalysis.uniqueWordsPercentage).toFixed(2) + '%' + : + '0%' + }
Unique words From 94e49e73fbbc91e3b6f20da2c93fba1b56746753 Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Thu, 25 Jul 2024 15:02:54 +0200 Subject: [PATCH 23/24] fixed/added to backend SEO metadata analysis testing --- wee/webscraper/src/seo-analysis/seo-analysis.service.spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wee/webscraper/src/seo-analysis/seo-analysis.service.spec.ts b/wee/webscraper/src/seo-analysis/seo-analysis.service.spec.ts index 0ce82e299..329da4a5e 100644 --- a/wee/webscraper/src/seo-analysis/seo-analysis.service.spec.ts +++ b/wee/webscraper/src/seo-analysis/seo-analysis.service.spec.ts @@ -57,6 +57,7 @@ describe('SeoAnalysisService', () => { expect(result).toEqual({ metaDescription: 'Test description', length: 16, + isUrlWordsInDescription: false, recommendations: 'Meta description length should be between 120 and 160 characters. Consider including words from the URL in the meta description: example.', }); }); @@ -69,6 +70,7 @@ describe('SeoAnalysisService', () => { expect(result).toEqual({ metaDescription: '', length: 0, + isUrlWordsInDescription: false, recommendations: 'Meta description length should be between 120 and 160 characters. Consider including words from the URL in the meta description: example.', }); }); @@ -82,6 +84,7 @@ describe('SeoAnalysisService', () => { expect(result).toEqual({ metaDescription: '', length: 0, + isUrlWordsInDescription: false, recommendations: 'Meta description length should be between 120 and 160 characters. Consider including words from the URL in the meta description: example.', }); }); @@ -94,6 +97,7 @@ describe('SeoAnalysisService', () => { expect(result).toEqual({ metaDescription: 'Test description', length: 16, + isUrlWordsInDescription: false, recommendations: 'Meta description length should be between 120 and 160 characters. Consider including words from the URL in the meta description: example.', }); }); From f62a9a2dcd12392513ed5db87e9ea79874e680bd Mon Sep 17 00:00:00 2001 From: MignonErasmus Date: Thu, 25 Jul 2024 15:29:02 +0200 Subject: [PATCH 24/24] commented out the recommendations and the respective tests for the onpage SEO analysis --- wee/frontend/specs/unit/Results.spec.tsx | 48 ++++++++++--------- wee/frontend/src/app/(pages)/results/page.tsx | 24 +++++----- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/wee/frontend/specs/unit/Results.spec.tsx b/wee/frontend/specs/unit/Results.spec.tsx index ad4d8ed54..b7f291062 100644 --- a/wee/frontend/specs/unit/Results.spec.tsx +++ b/wee/frontend/specs/unit/Results.spec.tsx @@ -450,8 +450,8 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.totalLinks)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.uniqueLinks)).toBeDefined(); - expect(screen.queryByTestId('internalLinking_recommendations')).toBeInTheDocument(); - expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.recommendations)).toBeDefined(); + // expect(screen.queryByTestId('internalLinking_recommendations')).toBeInTheDocument(); + // expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.recommendations)).toBeDefined(); }); }); @@ -482,7 +482,7 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.totalLinks)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.internalLinksAnalysis.uniqueLinks)).toBeDefined(); - expect(screen.queryByTestId('internalLinking_recommendations')).not.toBeInTheDocument(); + // expect(screen.queryByTestId('internalLinking_recommendations')).not.toBeInTheDocument(); }); }); @@ -497,8 +497,8 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.titleTag)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.length)).toBeDefined(); - expect(screen.queryByTestId('meta_recommendations')).toBeInTheDocument(); - expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.recommendations)).toBeDefined(); + // expect(screen.queryByTestId('meta_recommendations')).toBeInTheDocument(); + // expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.recommendations)).toBeDefined(); }); }); @@ -529,7 +529,7 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.metaDescriptionAnalysis.titleTag)).toBeDefined(); expect(screen.getByText('55')).toBeDefined(); - expect(screen.queryByTestId('meta_recommendations')).not.toBeInTheDocument(); + // expect(screen.queryByTestId('meta_recommendations')).not.toBeInTheDocument(); }); }); @@ -547,8 +547,8 @@ describe('Results Component', () => { expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.nonOptimizedCount)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.reasonsMap.format[0])).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.reasonsMap.format[2])).toBeDefined(); - expect(screen.queryByTestId('images_recommendations')).toBeInTheDocument(); - expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.recommendations)).toBeDefined(); + // expect(screen.queryByTestId('images_recommendations')).toBeInTheDocument(); + // expect(screen.getByText(mockResults[0].seoAnalysis.imageAnalysis.recommendations)).toBeDefined(); }); }); @@ -584,7 +584,10 @@ describe('Results Component', () => { fireEvent.click(SEOTab); await waitFor(() => { - expect(screen.queryByTestId('images_recommendations')).not.toBeInTheDocument(); + expect(screen.getByText('34')).toBeDefined(); + expect(screen.getByText('6')).toBeDefined(); + expect(screen.getByText('0')).toBeDefined(); + // expect(screen.queryByTestId('images_recommendations')).not.toBeInTheDocument(); }); }); @@ -600,8 +603,8 @@ describe('Results Component', () => { expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.metaDescription)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.length)).toBeDefined(); expect(screen.getByText('No')).toBeDefined(); - expect(screen.queryByTestId('titleTag_recommendations')).toBeInTheDocument(); - expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.recommendations)).toBeDefined(); + // expect(screen.queryByTestId('titleTag_recommendations')).toBeInTheDocument(); + // expect(screen.getByText(mockResults[0].seoAnalysis.titleTagsAnalysis.recommendations)).toBeDefined(); }); }); @@ -634,7 +637,7 @@ describe('Results Component', () => { expect(screen.getByText("Meta description for title tag analysis")).toBeDefined(); expect(screen.getByText("121")).toBeDefined(); expect(screen.getByText('Yes')).toBeDefined(); - expect(screen.queryByTestId('titleTag_recommendations')).not.toBeInTheDocument(); + // expect(screen.queryByTestId('titleTag_recommendations')).not.toBeInTheDocument(); }); }); @@ -650,7 +653,7 @@ describe('Results Component', () => { expect(screen.getByText(mockResults[0].seoAnalysis.uniqueContentAnalysis.textLength)).toBeDefined(); expect(screen.getByText('41.72%')).toBeDefined(); expect(screen.getByText('repeatedWordsOne: 19')).toBeDefined(); - expect(screen.queryByTestId('uniqueContent_recommendations')).not.toBeInTheDocument(); + // expect(screen.queryByTestId('uniqueContent_recommendations')).not.toBeInTheDocument(); }); }); @@ -688,9 +691,8 @@ describe('Results Component', () => { expect(screen.getByText(mockResults[0].seoAnalysis.uniqueContentAnalysis.textLength)).toBeDefined(); expect(screen.getByText('41.72%')).toBeDefined(); expect(screen.getByText('repeatedWordsOne: 19')).toBeDefined(); - - expect(screen.queryByTestId('uniqueContent_recommendations')).toBeInTheDocument(); - expect(screen.getByText('Content length should ideally be more than 500 characters.')).toBeDefined(); + // expect(screen.queryByTestId('uniqueContent_recommendations')).toBeInTheDocument(); + // expect(screen.getByText('Content length should ideally be more than 500 characters.')).toBeDefined(); }); }); @@ -723,8 +725,8 @@ describe('Results Component', () => { expect(screen.getByText(mockResults[0].seoAnalysis.uniqueContentAnalysis.textLength)).toBeDefined(); expect(screen.getByText('41.72%')).toBeDefined(); expect(screen.queryByText('repeatedWordsOne: 19')).not.toBeInTheDocument(); - expect(screen.queryByTestId('uniqueContent_recommendations')).toBeInTheDocument(); - expect(screen.getByText('Content length should ideally be more than 500 characters.')).toBeDefined(); + // expect(screen.queryByTestId('uniqueContent_recommendations')).toBeInTheDocument(); + // expect(screen.getByText('Content length should ideally be more than 500 characters.')).toBeDefined(); }); }); @@ -738,8 +740,8 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.count)).toBeDefined(); - expect(screen.queryByTestId('headings_recommendations')).toBeInTheDocument(); - expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.recommendations)).toBeDefined(); + // expect(screen.queryByTestId('headings_recommendations')).toBeInTheDocument(); + // expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.recommendations)).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.headings[0])).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.headings[1])).toBeDefined(); }); @@ -771,7 +773,7 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.count)).toBeDefined(); - expect(screen.queryByTestId('headings_recommendations')).not.toBeInTheDocument(); + // expect(screen.queryByTestId('headings_recommendations')).not.toBeInTheDocument(); expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.headings[0])).toBeDefined(); expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.headings[1])).toBeDefined(); }); @@ -803,8 +805,8 @@ describe('Results Component', () => { await waitFor(() => { expect(screen.getByText('0')).toBeDefined(); - expect(screen.queryByTestId('headings_recommendations')).toBeInTheDocument(); - expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.recommendations)).toBeDefined(); + // expect(screen.queryByTestId('headings_recommendations')).toBeInTheDocument(); + // expect(screen.getByText(mockResults[0].seoAnalysis.headingAnalysis.recommendations)).toBeDefined(); expect(screen.queryByText(mockResults[0].seoAnalysis.headingAnalysis.headings[0])).not.toBeInTheDocument(); expect(screen.queryByText(mockResults[0].seoAnalysis.headingAnalysis.headings[1])).not.toBeInTheDocument(); }); diff --git a/wee/frontend/src/app/(pages)/results/page.tsx b/wee/frontend/src/app/(pages)/results/page.tsx index ca46e2790..59bcecf8a 100644 --- a/wee/frontend/src/app/(pages)/results/page.tsx +++ b/wee/frontend/src/app/(pages)/results/page.tsx @@ -787,7 +787,7 @@ function ResultsComponent() {

{headingAnalysis?.count}

- { + {/* { headingAnalysis?.recommendations != '' &&
@@ -795,7 +795,7 @@ function ResultsComponent() {

{headingAnalysis?.recommendations}

- } + } */}
: <> @@ -843,7 +843,7 @@ function ResultsComponent() {
- { + {/* { internalLinkingAnalysis?.recommendations != '' &&
@@ -851,7 +851,7 @@ function ResultsComponent() {

{internalLinkingAnalysis?.recommendations}

- } + } */}
: <> @@ -892,7 +892,7 @@ function ResultsComponent() {

{metaDescriptionAnalysis?.length}

- { + {/* { metaDescriptionAnalysis?.recommendations !== '' && (
@@ -900,7 +900,7 @@ function ResultsComponent() {

{metaDescriptionAnalysis?.recommendations}

- )} + )} */}
: <> @@ -1005,7 +1005,7 @@ function ResultsComponent() {
} - { + {/* { imagesAnalysis?.recommendations != '' &&
@@ -1013,7 +1013,7 @@ function ResultsComponent() {

{imagesAnalysis?.recommendations}

- } + } */}
: <> @@ -1061,7 +1061,7 @@ function ResultsComponent() {

{titleTagsAnalysis?.isUrlWordsInDescription == true ? 'Yes' : 'No'}

- { + {/* { titleTagsAnalysis?.recommendations != '' &&
@@ -1069,7 +1069,7 @@ function ResultsComponent() {

{titleTagsAnalysis?.recommendations}

- } + } */}
: <> @@ -1143,7 +1143,7 @@ function ResultsComponent() {
- { + {/* { uniqContentAnalysis?.recommendations != '' &&
@@ -1151,7 +1151,7 @@ function ResultsComponent() {

{uniqContentAnalysis?.recommendations}

- } + } */}
: <>