diff --git a/README.md b/README.md index 4960cc8..4ad884e 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,16 @@ [![Scheduled CDN Upload](https://github.com/autonity/partner-registry/actions/workflows/scheduled-deployment.yaml/badge.svg)](https://github.com/autonity/partner-registry/actions/workflows/scheduled-deployment.yaml) [![Tests](https://github.com/autonity/partner-registry/actions/workflows/unit-tests.yaml/badge.svg?branch=develop)](https://github.com/autonity/partner-registry/actions/workflows/unit-tests.yaml) -# ECOSYSTEM Partner Registry - -How to add your project to our [Partner-Registry](https://github.com/autonity/partner-registry) repo. - -## Adding your project to the Partner Registry +# Adding your Project to the ECOSYSTEM Partner Registry Firstly you need to fork the registry [repo](https://github.com/autonity/partner-registry), clone it locally and open in your favorite editor. -#### Step 1: Select the `partners/` folder. +### Step 1: Select the `partners/` folder. -#### Step 2: In the `partners/` folder create a folder named according to your project. +### Step 2: In the `partners/` folder create a folder named according to your project. -#### Step 3: In your project folder you need to add three files: `thumbnail.png`, `banner.png` & `info.yaml`. - -The `info.yaml` file looks like this: +### Step 3: In your project folder you need to add light and dark images and a config file. +These are: +#### - info.yaml ```yaml name: 'Test Partner' # 20 characters max @@ -26,28 +22,18 @@ tags: # max 3 tags, the most relevant should be listed first url: 'https://www.autonity.org' # must be a valid https url ``` -The `thumbnail.png` must be no more than 196x160px. This is used when viewing your project in the grid list. -The `banner.png` must be no more than 274x105px. This is used when your product is featured and is in the carousel list. - -#### Tags should be added in order of relevance! The most relevant tag should be the first. - -#### Step 4: Create a pull-request containing your updated data. - -#### Step 5: Submit Your Pull Request - -**When you submit your PR, please use the following naming convention for the title:** - -- **Title Format**: `Add to Partner Registry` -- **Example**: `Add MyProject to Partner Registry` +#### - thumbnail_light.png & thumbnail_dark.png: +These must be **no more** than 196x160px. This is used when viewing your project in the grid list. -If you are successfull, your PR will have a `new-partner` label applied to it: +#### - banner_light.png & banner_dark.png +These must be **no more** than 274x105px. This is used when your product is featured and is in the carousel list. -![Success Bot Label](resources/tutorial/success-label.png) +### Step 4: Create a pull-request containing your updated data. -And a success comment +### Step 5: Submit Your Pull Request -![Success Bot Comment](resources/tutorial/success-bot-comment.png) +--- -If your PR failed validity checks, a bot will comment on the reasons why +If your PR fails validation checks, please check the github action, it will have the reason why. This will be in the **Check partners are valid** section of the github action. -![Failure bot comment](resources/tutorial/failure-bot-comment.png) +![Failure bot comment](resources/tutorial/gha.png) diff --git a/package.json b/package.json index 787c102..4653c4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autonity-partner-registry", - "version": "1.0.0", + "version": "1.0.5", "description": "Sandbox for testing Github features relevant to Engineering", "scripts": { "build": "npx tsc", @@ -45,4 +45,4 @@ "js-yaml": "^4.1.0", "sharp": "^0.33.5" } -} +} \ No newline at end of file diff --git a/partners/Stakeflow/banner.png b/partners/Stakeflow/banner_dark.png similarity index 100% rename from partners/Stakeflow/banner.png rename to partners/Stakeflow/banner_dark.png diff --git a/partners/Stakeflow/banner_light.png b/partners/Stakeflow/banner_light.png new file mode 100644 index 0000000..4459a49 Binary files /dev/null and b/partners/Stakeflow/banner_light.png differ diff --git a/partners/Stakeflow/thumbnail.png b/partners/Stakeflow/thumbnail.png deleted file mode 100644 index 471ec4e..0000000 Binary files a/partners/Stakeflow/thumbnail.png and /dev/null differ diff --git a/partners/Stakeflow/thumbnail_dark.png b/partners/Stakeflow/thumbnail_dark.png new file mode 100644 index 0000000..05e0ed9 Binary files /dev/null and b/partners/Stakeflow/thumbnail_dark.png differ diff --git a/partners/Stakeflow/thumbnail_light.png b/partners/Stakeflow/thumbnail_light.png new file mode 100644 index 0000000..14d8cc2 Binary files /dev/null and b/partners/Stakeflow/thumbnail_light.png differ diff --git a/partners/SubQuery Decentralised RPCs/banner.png b/partners/SubQuery Decentralised RPCs/banner_dark.png similarity index 100% rename from partners/SubQuery Decentralised RPCs/banner.png rename to partners/SubQuery Decentralised RPCs/banner_dark.png diff --git a/partners/SubQuery Decentralised RPCs/banner_light.png b/partners/SubQuery Decentralised RPCs/banner_light.png new file mode 100644 index 0000000..1a4502e Binary files /dev/null and b/partners/SubQuery Decentralised RPCs/banner_light.png differ diff --git a/partners/SubQuery Decentralised RPCs/thumbnail.png b/partners/SubQuery Decentralised RPCs/thumbnail_dark.png similarity index 100% rename from partners/SubQuery Decentralised RPCs/thumbnail.png rename to partners/SubQuery Decentralised RPCs/thumbnail_dark.png diff --git a/partners/SubQuery Decentralised RPCs/thumbnail_light.png b/partners/SubQuery Decentralised RPCs/thumbnail_light.png new file mode 100644 index 0000000..040543b Binary files /dev/null and b/partners/SubQuery Decentralised RPCs/thumbnail_light.png differ diff --git a/partners/VIA Labs/banner.png b/partners/VIA Labs/banner_dark.png similarity index 100% rename from partners/VIA Labs/banner.png rename to partners/VIA Labs/banner_dark.png diff --git a/partners/VIA Labs/banner_light.png b/partners/VIA Labs/banner_light.png new file mode 100644 index 0000000..b49d6fc Binary files /dev/null and b/partners/VIA Labs/banner_light.png differ diff --git a/partners/VIA Labs/thumbnail.png b/partners/VIA Labs/thumbnail.png deleted file mode 100644 index 57f8285..0000000 Binary files a/partners/VIA Labs/thumbnail.png and /dev/null differ diff --git a/partners/VIA Labs/thumbnail_dark.png b/partners/VIA Labs/thumbnail_dark.png new file mode 100644 index 0000000..85402cf Binary files /dev/null and b/partners/VIA Labs/thumbnail_dark.png differ diff --git a/partners/VIA Labs/thumbnail_light.png b/partners/VIA Labs/thumbnail_light.png new file mode 100644 index 0000000..e664677 Binary files /dev/null and b/partners/VIA Labs/thumbnail_light.png differ diff --git a/partners/Web3CDN/banner.png b/partners/Web3CDN/banner_dark.png similarity index 100% rename from partners/Web3CDN/banner.png rename to partners/Web3CDN/banner_dark.png diff --git a/partners/Web3CDN/banner_light.png b/partners/Web3CDN/banner_light.png new file mode 100644 index 0000000..db04635 Binary files /dev/null and b/partners/Web3CDN/banner_light.png differ diff --git a/partners/Web3CDN/thumbnail.png b/partners/Web3CDN/thumbnail.png deleted file mode 100644 index 6d369c9..0000000 Binary files a/partners/Web3CDN/thumbnail.png and /dev/null differ diff --git a/partners/Web3CDN/thumbnail_dark.png b/partners/Web3CDN/thumbnail_dark.png new file mode 100644 index 0000000..2c654c3 Binary files /dev/null and b/partners/Web3CDN/thumbnail_dark.png differ diff --git a/partners/Web3CDN/thumbnail_light.png b/partners/Web3CDN/thumbnail_light.png new file mode 100644 index 0000000..a97c347 Binary files /dev/null and b/partners/Web3CDN/thumbnail_light.png differ diff --git a/partners/chorus-one/banner.png b/partners/chorus-one/banner_dark.png similarity index 100% rename from partners/chorus-one/banner.png rename to partners/chorus-one/banner_dark.png diff --git a/partners/chorus-one/banner_light.png b/partners/chorus-one/banner_light.png new file mode 100644 index 0000000..7384dec Binary files /dev/null and b/partners/chorus-one/banner_light.png differ diff --git a/partners/chorus-one/thumbnail.png b/partners/chorus-one/thumbnail_dark.png similarity index 100% rename from partners/chorus-one/thumbnail.png rename to partners/chorus-one/thumbnail_dark.png diff --git a/partners/chorus-one/thumbnail_light.png b/partners/chorus-one/thumbnail_light.png new file mode 100644 index 0000000..7b1183a Binary files /dev/null and b/partners/chorus-one/thumbnail_light.png differ diff --git a/partners/example/README.md b/partners/example/README.md deleted file mode 100644 index efb437a..0000000 --- a/partners/example/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# ECOSYSTEM Partner Registry - -How to add your project to our [Partner-Registry](https://github.com/autonity/partner-registry) repo. - -## Adding your project to the Partner Registry - -Firstly you need to fork the registry [repo](https://github.com/autonity/partner-registry), clone it locally and open in your favorite editor. - -#### Step 1: Select the `partners/` folder. - -#### Step 2: In the `partners/` folder create a folder named according to your project. - -#### Step 3: In your project folder you need to add three files: `thumbnail.png`, `banner.png` & `info.yaml`. - -The `info.yaml` file looks like this: - -```yaml -name: 'Partner' # 20 characters max -short_description: 'A short description' # 60 character max description -long_description: 'A long description used when the partner is featured' # 175 character max -tags: # max 3 tags, the most relevant should be listed first - - 'defi' # no more then 12 characters per tag - - 'infra' -url: 'https://www.autonity.org' # must be a valid https url -``` - -**Notes** - -- The `thumbnail.png` must be no more than 196x160px. -- The `banner.png` must be no more than 274x105px. -- Tags should be added in order of relevance! The most relevant tag should be the first. - -#### Step 4: Create a pull-request containing your updated data. - -#### Step 5: Submit Your Pull Request - -**When you submit your PR, please use the following naming convention for the title:** - -- **Title Format**: `Add to Partner Registry` -- **Example**: `Add MyProject to Partner Registry` - -If you are successfull, your PR will have a `new-partner` label applied to it: - -![Success Bot Label](../../resources/tutorial/success-label.png) - -And a success comment - -![Success Bot Comment](../../resources/tutorial/success-bot-comment.png) - -If your PR failed validity checks, a bot will comment on the reasons why - -![Failure bot comment](../../resources/tutorial/failure-bot-comment.png) diff --git a/partners/example/banner_dark.png b/partners/example/banner_dark.png new file mode 100644 index 0000000..e899dbc Binary files /dev/null and b/partners/example/banner_dark.png differ diff --git a/partners/example/banner_light.png b/partners/example/banner_light.png new file mode 100644 index 0000000..d4c5ce1 Binary files /dev/null and b/partners/example/banner_light.png differ diff --git a/partners/example/banner.png b/partners/example/thumbnail_dark.png similarity index 100% rename from partners/example/banner.png rename to partners/example/thumbnail_dark.png diff --git a/partners/example/thumbnail.png b/partners/example/thumbnail_light.png similarity index 100% rename from partners/example/thumbnail.png rename to partners/example/thumbnail_light.png diff --git a/resources/tutorial/failure-bot-comment.png b/resources/tutorial/failure-bot-comment.png deleted file mode 100644 index 399ef69..0000000 Binary files a/resources/tutorial/failure-bot-comment.png and /dev/null differ diff --git a/resources/tutorial/gha.png b/resources/tutorial/gha.png new file mode 100644 index 0000000..6b5d38e Binary files /dev/null and b/resources/tutorial/gha.png differ diff --git a/resources/tutorial/success-bot-comment.png b/resources/tutorial/success-bot-comment.png deleted file mode 100644 index 308b46d..0000000 Binary files a/resources/tutorial/success-bot-comment.png and /dev/null differ diff --git a/resources/tutorial/success-label.png b/resources/tutorial/success-label.png deleted file mode 100644 index f265d60..0000000 Binary files a/resources/tutorial/success-label.png and /dev/null differ diff --git a/scripts/constants.ts b/scripts/constants.ts index 06daf80..f776a7b 100644 --- a/scripts/constants.ts +++ b/scripts/constants.ts @@ -1,6 +1,9 @@ export const partnerFileName = 'info.yaml' -export const defaultThumbnailName = 'thumbnail.png' -export const defaultBannerName = 'banner.png' + +export const defaultThumbnailNameLight = 'thumbnail_light.png' +export const defaultBannerNameLight = 'banner_light.png' +export const defaultThumbnailNameDark = 'thumbnail_dark.png' +export const defaultBannerNameDark = 'banner_dark.png' export const partnerDir = '../partners' export const partnerStorageName = 'partners.json' export const examplePartnerName = 'example' @@ -9,7 +12,7 @@ export const longDescriptionLimit = 175 export const nameLimit = 20 export const tagCharacterLimit = 12 export const maxNumberOfTags = 3 -export const maxThumbnailWidth = 196 +export const maxThumbnailWidth = 160 export const maxThumbnailHeight = 160 export const maxBannerWidth = 274 export const maxBannerHeight = 105 diff --git a/scripts/partners.json b/scripts/partners.json deleted file mode 100644 index fe51488..0000000 --- a/scripts/partners.json +++ /dev/null @@ -1 +0,0 @@ -[] diff --git a/scripts/upload-layer.ts b/scripts/upload-layer.ts index 82c5fb8..fb63304 100644 --- a/scripts/upload-layer.ts +++ b/scripts/upload-layer.ts @@ -1,11 +1,14 @@ -import { S3Client, PutObjectCommand, ListObjectsV2Command, DeleteObjectCommand } from '@aws-sdk/client-s3' -import dotenv from 'dotenv' -import { buildPartnersJson, getPartnerDirectories } from './generate-partners' -import { validatePartnerInfo } from './validate-partners' -import path from 'path' import * as fs from 'fs' + +import { DeleteObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from '@aws-sdk/client-s3' +import { buildPartnersJson, getPartnerDirectories } from './generate-partners' + +import dotenv from 'dotenv' import { getJsonfromYaml } from './translation-layer' import { partnerFileName } from './constants' +import path from 'path' +import { validatePartnerInfo } from './validate-partners' + dotenv.config() const REGION = process.env.REGION ?? '' @@ -26,9 +29,9 @@ const cleanupUnusedImages = async (s3Client: S3Client, currentPartnerNames: Set< Bucket: BUCKET_NAME, Prefix: 'images/' } - + const listedObjects = await s3Client.send(new ListObjectsV2Command(listParams)) - + if (!listedObjects.Contents) return for (const item of listedObjects.Contents) { @@ -81,7 +84,7 @@ const run = async () => { console.log('Proceeding with upload...') const partners = buildPartnersJson(partnerDirectories) const currentPartnerNames = new Set(partners.map(partner => partner.name)) - + const uploadParams = { Bucket: BUCKET_NAME, Key: OBJECT_KEY, @@ -90,18 +93,38 @@ const run = async () => { } const data = await s3Client.send(new PutObjectCommand(uploadParams)) console.log(`Partners uploaded. ETag: ${data.ETag}`) - + console.log('Proceeding with image uploads...') for (const partnerDir of partnerDirectories) { - const {name } = getJsonfromYaml(`${partnerDir}/${partnerFileName}`) - const thumbnail = path.join(partnerDir, 'thumbnail.png') - const thumbnailKey = `images/${name}/thumbnail.png` - - const banner = path.join(partnerDir, 'banner.png') - const bannerKey = `images/${name}/banner.png` - - await uploadFile(s3Client, thumbnail, thumbnailKey) - await uploadFile(s3Client, banner, bannerKey) + const { name } = getJsonfromYaml(`${partnerDir}/${partnerFileName}`) + + console.log('Uploading images for:', name) + + // Light thumbnail + const thumbnailLight = path.join(partnerDir, 'thumbnail_light.png') + const thumbnailLightKey = `images/${name}/thumbnail_light.png` + + // Dark thumbnail + const thumbnailDark = path.join(partnerDir, 'thumbnail_dark.png') + const thumbnailDarkKey = `images/${name}/thumbnail_dark.png` + + // Light Banner + const bannerLight = path.join(partnerDir, 'banner_light.png') + const bannerLightKey = `images/${name}/banner_light.png` + + // Dark Banner + const bannerDark = path.join(partnerDir, 'banner_dark.png') + const bannerDarkKey = `images/${name}/banner_dark.png` + + //upload light thumbnail + await uploadFile(s3Client, thumbnailLight, thumbnailLightKey) + //upload dark thumbnail + await uploadFile(s3Client, thumbnailDark, thumbnailDarkKey) + + //upload light banner + await uploadFile(s3Client, bannerLight, bannerLightKey) + //upload dark banner + await uploadFile(s3Client, bannerDark, bannerDarkKey) } console.log('Cleaning up unused images...') diff --git a/scripts/validate-partners.ts b/scripts/validate-partners.ts index 4f30eb2..977c4cb 100644 --- a/scripts/validate-partners.ts +++ b/scripts/validate-partners.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { defaultBannerName, defaultThumbnailName, longDescriptionLimit, maxBannerHeight, maxBannerWidth, maxNumberOfTags, maxThumbnailHeight, maxThumbnailWidth, nameLimit, partnerFileName, shortDescriptionLimit, tagCharacterLimit } from './constants'; +import { defaultBannerNameDark, defaultBannerNameLight, defaultThumbnailNameDark, defaultThumbnailNameLight, longDescriptionLimit, maxBannerHeight, maxBannerWidth, maxNumberOfTags, maxThumbnailHeight, maxThumbnailWidth, nameLimit, partnerFileName, shortDescriptionLimit, tagCharacterLimit } from './constants'; import { getPartnerDirectories, getPartnerObject } from './generate-partners'; import { Partner } from './interface'; @@ -11,7 +11,7 @@ const requiredFields: (keyof Partner)[] = ['name', 'shortDescription', 'longDesc /** * Type guard to check if an object conforms to the Partner interface. - * + * * @param {any} obj - The object to be checked. * @returns {obj is Partner} - Returns true if the object matches the Partner interface. */ @@ -32,11 +32,12 @@ export function isPartner(obj: any): obj is Partner { export async function validatePartnerInfo(partnerPath: string): Promise { const fullPartnerPath = path.join(partnerPath, partnerFileName); - const fullThumbnailPath = path.join(partnerPath, defaultThumbnailName); - const fullBannerPath = path.join(partnerPath, defaultBannerName); + const fullThumbnailPathLight = path.join(partnerPath, defaultThumbnailNameLight); + const fullBannerPathLight = path.join(partnerPath, defaultBannerNameLight); + const fullThumbnailPathDark = path.join(partnerPath, defaultThumbnailNameDark); + const fullBannerPathDark = path.join(partnerPath, defaultBannerNameDark); let errorMessages: string[] = []; - if (!fs.existsSync(fullPartnerPath)) { errorMessages.push(`${partnerFileName} is missing in ${partnerPath}`); return errorMessages; @@ -51,24 +52,43 @@ export async function validatePartnerInfo(partnerPath: string): Promise} - Returns a promise that resolves with an array of error messages if the image is invalid. */ -async function checkImageDimensions(filePath: string, maxWidth = maxThumbnailWidth, maxHeight = maxThumbnailHeight): Promise { +export async function checkImageDimensions(filePath: string, maxWidth = maxThumbnailWidth, maxHeight = maxThumbnailHeight): Promise { const errorMessages: string[] = []; try { const { width, height } = await sharp(filePath).metadata(); @@ -103,6 +123,31 @@ async function checkImageDimensions(filePath: string, maxWidth = maxThumbnailWid } } +/** + * Checks if the image at the given file path matches the exact width and height. + * + * @param {string} filePath - The file path to the image. + * @param {number} exactWidth - The required width of the image. + * @param {number} exactHeight - The required height of the image. + * @returns {Promise} - Returns a promise that resolves with an array of error messages if the image does not match the exact dimensions. + */ +export async function checkImageDimensionsExact(filePath: string, exactWidth: number, exactHeight: number): Promise { + const errorMessages: string[] = []; + try { + const { width, height } = await sharp(filePath).metadata(); + + if (!width || !height) { + errorMessages.push(`Image metadata could not be read for ${filePath}`); + } else if (width !== exactWidth || height !== exactHeight) { + errorMessages.push(`Image dimensions for ${filePath} are ${width}x${height}, but expected ${exactWidth}x${exactHeight}`); + } + } catch (error) { + errorMessages.push(`Error reading image metadata for ${filePath}: ${error}`); + } + return errorMessages; +} + + /** * Validates partner fields such as name, description, tags, and URL. * diff --git a/tests/partners/autonity/README.md b/tests/partners/autonity/README.md deleted file mode 100644 index bca981d..0000000 --- a/tests/partners/autonity/README.md +++ /dev/null @@ -1,21 +0,0 @@ -### This is an example partner - -To submit your own project, add a project folder to the `partners/` directory. - -#### In your project folder you need to add three files: `thumbnail.png` `banner.png` & `info.json`. - -### The thumbnail.png file must be no more then 125x125px -### The banner.png file must be no more then 300x533px - - -The `info.json` file looks like this: - -``` typescript -export interface Partner { - name: string; //40 characters max - shortDescription: string; // 75 character max description - longDescription: string; // 250 character max description - tags: string[]; //keywords that best describe your project, max 5 each one no more then 20 characters - url: string; // must be a https url -} -``` diff --git a/tests/validate-partner.test.ts b/tests/validate-partner.test.ts deleted file mode 100644 index 07f39f6..0000000 --- a/tests/validate-partner.test.ts +++ /dev/null @@ -1,442 +0,0 @@ -// __tests__/validatePartnerInfo.test.ts -import { longDescriptionLimit, maxBannerHeight, maxBannerWidth, maxNumberOfTags, maxThumbnailHeight, maxThumbnailWidth, nameLimit, shortDescriptionLimit, tagCharacterLimit } from '../scripts/constants'; - -import fs from "fs-extra"; -import mockFs from 'mock-fs'; -import sharp from 'sharp'; -import { validatePartnerInfo } from '../scripts/validate-partners'; // Adjust the path as needed - -jest.mock('sharp'); -jest.mock("fs-extra"); -describe('validatePartnerInfo', () => { - const mockedSharp = sharp as jest.MockedFunction; - const partnerPath = '/path/to/partner'; - - const mockResults = { - 'info.yaml': ` - name: "Mock Partner" - badge: "mockBadge" - short_description: "Mock short description" - long_description: "Mock long description" - tags: - - "mock" - - "partner" - url: "https://mockpartner.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - beforeEach(() => { - jest.resetAllMocks(); - }); - - afterEach(() => { - mockFs.restore(); - }); - - it('should return an an array of error messages with one message, if path does not exist', async () => { - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`info.yaml is missing in ${partnerPath}`]); - } - ); - - it('should error if a required field is missing', async () => { - mockFs({ - [partnerPath]: { - 'info.yaml': ` - name: "Mock Partner" - badge: "mockBadge" - short_description: "Mock short description" - long_description: "Mock long description" - tags: - - "mock" - - "partner" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`url is missing in ${partnerPath}/info.yaml`]); - }) - - it('should error if unable to find images', async () => { - - const imagesMissing = { - 'info.yaml': ` - name: "Mock Partner" - badge: "mockBadge" - short_description: "Mock short description" - long_description: "Mock long description" - tags: - - "mock" - - "partner" - url: "https://mockpartner.com" - featured: false - ` - } - - mockFs({ - [partnerPath]: imagesMissing, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual(["Thumbnail image is missing", "Banner image is missing"]); - }); - - it('should log to the user an error if unable to read image dimensions', async () => { - - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({}), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual(["image metadata could not be read for /path/to/partner/thumbnail.png", "image metadata could not be read for /path/to/partner/banner.png"]); - }); - - it('should log to the user an error if unable to read image metadata', async () => { - - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockImplementation(() => { - throw new Error('Error reading image metadata'); - }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual(["Error reading image metadata","Error reading image metadata"]); - }); - - - it('should return an a log to the user if the name exceeds a character length', async () => { - const mockResults = { - 'info.yaml': ` - name: "Mock Partner really long name that exceeds the character limit" - badge: "mockBadge" - short_description: "Mock short description" - long_description: "Mock long description" - tags: - - "mock" - - "partner" - url: "https://mockpartner.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`'name' exceeds ${nameLimit} characters`]); - }); - - it('should return an error if info.yaml is missing', async () => { - mockFs({ - [partnerPath]: { - 'logo.png': 'fake-image-data', - 'README.md': 'Some readme content' - } - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - - expect(errors).toEqual(["info.yaml is missing in /path/to/partner"]); - }); - - it('should return no errors for valid partner info', async () => { - mockFs({ - [partnerPath]: mockResults - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - - expect(errors).toHaveLength(0); - }); - - it('should return an error if tag char limit is reached', async () => { - const badTags = { - 'info.yaml': ` - name: "Mock Partner" - badge: "mockBadge" - short_description: "Mock short description" - long_description: "Mock long description" - tags: - - "mock" - - "partner with long tags" - url: "https://mockpartner.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - mockFs({ - [partnerPath]: badTags - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`'tag' exceeds ${tagCharacterLimit} characters`]); - }); - - it('should return an error for invalid attributes in info.yaml', async () => { - - mockFs({ - [partnerPath]: { - 'info.yaml': {} - } - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual( ["info.yaml contains invalid data in /path/to/partner"]); - }); - - it('should error if image (banner) dimensions are too big', async () => { - mockFs({ - [partnerPath]: mockResults - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 274, height: 105 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - const expectedErrors = [`image dimensions exceed ${maxThumbnailWidth}x${maxThumbnailHeight} pixels`] - expect(errors).toEqual(expectedErrors); - - }); - - it('should return an a log to the user if the long description exceeds a character length', async () => { - const mockResults = { - 'info.yaml': ` - name: "name" - badge: "mockBadge" - short_description: "Mock short description" - long_description: "This is a really long description that exceeds the character limit so that we can indeed test the way in which the script handles descriptions that indeed do exceed expectations." - tags: - - "mock" - - "partner" - url: "https://mockpartner.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`'long_description' exceeds ${longDescriptionLimit} characters`]); - }); - - it('should return an a log to the user if the short description exceeds a character length', async () => { - const mockResults = { - 'info.yaml': ` - name: "name" - badge: "mockBadge" - short_description: "This is a really long description that exceeds the character limit so that we can indeed test the way in which the script handles descriptions that indeed do exceed expectations." - long_description: "long desc" - tags: - - "mock" - - "partner" - url: "https://mockpartner.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`'short_description' exceeds ${shortDescriptionLimit} characters`]); - }); - - it('Partner must have atleast one tag', async () => { - const mockResults = { - 'info.yaml': ` - name: "name" - badge: "mockBadge" - short_description: "short desc" - long_description: "long desc" - tags: [] - url: "https://mockpartner.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`'tags' should be a non-empty array`]); - }); - - it('should not exceed the maximum number of tags', async () => { - const mockResults = { - 'info.yaml': ` - name: "name" - badge: "mockBadge" - short_description: "short desc" - long_description: "long desc" - tags: - - tag 1 - - tag 2 - - tag 3 - - tag 4 - - tag 5 - - tag 6 - url: "https://mockpartner.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`'tags' should not contain more than ${maxNumberOfTags} tags`]); - }); - - it('should log if provided url is not https', async () => { - const mockResults = { - 'info.yaml': ` - name: "name" - badge: "mockBadge" - short_description: "short desc" - long_description: "long desc" - tags: - - tag 1 - - tag 2 - - tag 3 - url: "http://mockpartner.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const errors = await validatePartnerInfo(partnerPath); - expect(errors).toEqual([`'url' should be a HTTPS`]); - }); - - it('should log if it couldn\'t parse the url', async () => { - const mockResults = { - 'info.yaml': ` - name: "name" - badge: "mockBadge" - short_description: "short desc" - long_description: "long desc" - tags: - - tag 1 - - tag 2 - - tag 3 - url: "https://forcefail.com" - featured: false - `, - 'banner.png': 'fake-image-data', - 'thumbnail.png': 'fake-image-data', - } - - mockFs({ - [partnerPath]: mockResults, - - }); - - mockedSharp.mockReturnValue({ - metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), - } as any); - - const originalURL = global.URL; // Save original URL - //@ts-ignore - global.URL = jest.fn(() => { - console.log('Mocked URL constructor called'); - throw new Error("Invalid URL"); - }); - - const errors = await validatePartnerInfo(partnerPath); - console.log('Errors:', errors); - - expect(errors).toEqual([`'url' is not a valid URL`]); - - global.URL = originalURL; - }); - -});