-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #195 from HubSpot/add/cms-tests
chore: Add tests for CMS utils
- Loading branch information
Showing
5 changed files
with
496 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import fs, { PathLike } from 'fs-extra'; | ||
import findup from 'findup-sync'; | ||
import { getCwd } from '../path'; | ||
import { downloadGithubRepoContents } from '../github'; | ||
import { | ||
createFunction, | ||
isObjectOrFunction, | ||
createEndpoint, | ||
createConfig, | ||
} from '../cms/functions'; | ||
|
||
jest.mock('fs-extra'); | ||
jest.mock('findup-sync'); | ||
jest.mock('../path'); | ||
jest.mock('../github'); | ||
|
||
const mockedGetCwd = getCwd as jest.MockedFunction<typeof getCwd>; | ||
const mockedFindup = findup as jest.MockedFunction<typeof findup>; | ||
const mockedFsExistsSync = fs.existsSync as jest.MockedFunction< | ||
typeof fs.existsSync | ||
>; | ||
const mockedFsReadFileSync = fs.readFileSync as jest.MockedFunction< | ||
typeof fs.readFileSync | ||
>; | ||
|
||
describe('lib/cms/functions', () => { | ||
describe('createFunction', () => { | ||
const mockFunctionInfo = { | ||
functionsFolder: 'testFolder', | ||
filename: 'testFunction', | ||
endpointPath: '/api/test', | ||
endpointMethod: 'GET', | ||
}; | ||
const mockDest = '/mock/dest'; | ||
|
||
beforeEach(() => { | ||
mockedGetCwd.mockReturnValue('/mock/cwd'); | ||
mockedFindup.mockReturnValue(null); | ||
|
||
// Set up fs.existsSync to return different values for different paths | ||
mockedFsExistsSync.mockImplementation((path: PathLike) => { | ||
if (path === '/mock/dest/testFolder.functions/testFunction.js') { | ||
return false; | ||
} | ||
if (path === '/mock/dest/testFolder.functions/serverless.json') { | ||
return true; | ||
} | ||
if (path === '/mock/dest/testFolder.functions') { | ||
return true; | ||
} | ||
return false; | ||
}); | ||
|
||
// Mock fs.readFileSync to return a valid JSON for the config file | ||
mockedFsReadFileSync.mockReturnValue( | ||
JSON.stringify({ | ||
endpoints: {}, | ||
}) | ||
); | ||
}); | ||
|
||
it('should create a new function successfully', async () => { | ||
await createFunction(mockFunctionInfo, mockDest); | ||
|
||
expect(fs.mkdirp).not.toHaveBeenCalled(); | ||
|
||
expect(downloadGithubRepoContents).toHaveBeenCalledWith( | ||
'HubSpot/cms-sample-assets', | ||
'functions/sample-function.js', | ||
'/mock/dest/testFolder.functions/testFunction.js' | ||
); | ||
|
||
// Check that the config file was updated | ||
expect(fs.writeFileSync).toHaveBeenCalledWith( | ||
'/mock/dest/testFolder.functions/serverless.json', | ||
expect.any(String) | ||
); | ||
}); | ||
}); | ||
|
||
describe('isObjectOrFunction', () => { | ||
it('should return true for objects', () => { | ||
expect(isObjectOrFunction({})).toBe(true); | ||
}); | ||
|
||
it('should return true for functions', () => { | ||
// eslint-disable-next-line @typescript-eslint/no-empty-function | ||
expect(isObjectOrFunction(() => {})).toBe(true); | ||
}); | ||
|
||
it('should return false for null', () => { | ||
// @ts-expect-error test case | ||
expect(isObjectOrFunction(null)).toBe(false); | ||
}); | ||
|
||
it('should return false for primitives', () => { | ||
// @ts-expect-error test case | ||
expect(isObjectOrFunction(5)).toBe(false); | ||
// @ts-expect-error test case | ||
expect(isObjectOrFunction('string')).toBe(false); | ||
// @ts-expect-error test case | ||
expect(isObjectOrFunction(true)).toBe(false); | ||
}); | ||
}); | ||
|
||
describe('createEndpoint', () => { | ||
it('should create an endpoint object', () => { | ||
const result = createEndpoint('POST', 'test.js'); | ||
expect(result).toEqual({ | ||
method: 'POST', | ||
file: 'test.js', | ||
}); | ||
}); | ||
|
||
it('should default to GET method if not provided', () => { | ||
const result = createEndpoint('', 'test.js'); | ||
expect(result).toEqual({ | ||
method: 'GET', | ||
file: 'test.js', | ||
}); | ||
}); | ||
}); | ||
|
||
describe('createConfig', () => { | ||
it('should create a config object', () => { | ||
const result = createConfig({ | ||
endpointPath: '/api/test', | ||
endpointMethod: 'POST', | ||
functionFile: 'test.js', | ||
}); | ||
|
||
expect(result).toEqual({ | ||
runtime: 'nodejs18.x', | ||
version: '1.0', | ||
environment: {}, | ||
secrets: [], | ||
endpoints: { | ||
'/api/test': { | ||
method: 'POST', | ||
file: 'test.js', | ||
}, | ||
}, | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import findup from 'findup-sync'; | ||
import { getHubSpotWebsiteOrigin } from '../urls'; | ||
import { getThemeJSONPath, getThemePreviewUrl } from '../cms/themes'; | ||
import { getEnv } from '../../config'; | ||
import { ENVIRONMENTS } from '../../constants/environments'; | ||
|
||
jest.mock('findup-sync'); | ||
jest.mock('../urls'); | ||
jest.mock('../../config'); | ||
jest.mock('../../constants/environments', () => ({ | ||
ENVIRONMENTS: { | ||
PROD: 'https://prod.hubspot.com', | ||
QA: 'https://qa.hubspot.com', | ||
}, | ||
})); | ||
|
||
const mockedFindup = findup as jest.MockedFunction<typeof findup>; | ||
const mockedGetEnv = getEnv as jest.MockedFunction<typeof getEnv>; | ||
const mockedGetHubSpotWebsiteOrigin = | ||
getHubSpotWebsiteOrigin as jest.MockedFunction< | ||
typeof getHubSpotWebsiteOrigin | ||
>; | ||
|
||
describe('lib/cms/themes', () => { | ||
describe('getThemeJSONPath', () => { | ||
it('should return the theme.json path if found', () => { | ||
mockedFindup.mockReturnValue('/path/to/theme.json'); | ||
|
||
const result = getThemeJSONPath('/some/path'); | ||
|
||
expect(findup).toHaveBeenCalledWith('theme.json', { | ||
cwd: '/some/path', | ||
nocase: true, | ||
}); | ||
expect(result).toBe('/path/to/theme.json'); | ||
}); | ||
|
||
it('should return null if theme.json is not found', () => { | ||
mockedFindup.mockReturnValue(null); | ||
|
||
const result = getThemeJSONPath('/some/path'); | ||
|
||
expect(findup).toHaveBeenCalledWith('theme.json', { | ||
cwd: '/some/path', | ||
nocase: true, | ||
}); | ||
expect(result).toBeNull(); | ||
}); | ||
}); | ||
|
||
describe('getThemePreviewUrl', () => { | ||
it('should return the correct theme preview URL for PROD environment', () => { | ||
mockedFindup.mockReturnValue('/src/my-theme/theme.json'); | ||
mockedGetEnv.mockReturnValue('prod'); | ||
mockedGetHubSpotWebsiteOrigin.mockReturnValue('https://prod.hubspot.com'); | ||
|
||
const result = getThemePreviewUrl('/path/to/file', 12345); | ||
|
||
expect(getEnv).toHaveBeenCalledWith(12345); | ||
expect(getHubSpotWebsiteOrigin).toHaveBeenCalledWith(ENVIRONMENTS.PROD); | ||
expect(result).toBe( | ||
'https://prod.hubspot.com/theme-previewer/12345/edit/my-theme' | ||
); | ||
}); | ||
|
||
it('should return the correct theme preview URL for QA environment', () => { | ||
mockedFindup.mockReturnValue('/src/my-theme/theme.json'); | ||
mockedGetEnv.mockReturnValue('qa'); | ||
mockedGetHubSpotWebsiteOrigin.mockReturnValue('https://qa.hubspot.com'); | ||
|
||
const result = getThemePreviewUrl('/path/to/file', 12345); | ||
|
||
expect(getEnv).toHaveBeenCalledWith(12345); | ||
expect(getHubSpotWebsiteOrigin).toHaveBeenCalledWith(ENVIRONMENTS.QA); | ||
expect(result).toBe( | ||
'https://qa.hubspot.com/theme-previewer/12345/edit/my-theme' | ||
); | ||
}); | ||
|
||
it('should return undefined if theme.json is not found', () => { | ||
mockedFindup.mockReturnValue(null); | ||
|
||
const result = getThemePreviewUrl('/invalid/path', 12345); | ||
|
||
expect(result).toBeUndefined(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import fs, { Stats } from 'fs-extra'; | ||
import { validateHubl } from '../../api/validateHubl'; | ||
import { walk } from '../fs'; | ||
import { lint } from '../cms/validate'; | ||
import { LintResult, Validation } from '../../types/HublValidation'; | ||
import { AxiosPromise } from 'axios'; | ||
|
||
jest.mock('fs-extra'); | ||
jest.mock('../../api/validateHubl'); | ||
jest.mock('../fs'); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const mockedFsStat = fs.stat as jest.MockedFunction<any>; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const mockedFsReadFile = fs.readFile as jest.MockedFunction<any>; | ||
const mockedWalk = walk as jest.MockedFunction<typeof walk>; | ||
const mockedValidateHubl = validateHubl as jest.MockedFunction< | ||
typeof validateHubl | ||
>; | ||
|
||
const mockFsStats = jest.createMockFromModule<Stats>('fs-extra'); | ||
|
||
mockFsStats.isDirectory = jest.fn(() => true); | ||
|
||
const mockValidation: Validation = { | ||
meta: { | ||
all_widgets: [], | ||
widgets_in_rich_text: [], | ||
editable_flex_areas: [], | ||
editable_layout_sections: null, | ||
email_style_settings: null, | ||
sms_flex_area: [], | ||
google_font_variations: null, | ||
custom_font_variations: [], | ||
has_style_tag: false, | ||
has_header_tag: false, | ||
output_html: '', | ||
has_menu_tag: false, | ||
has_theme_setting_function: false, | ||
template_source: '', | ||
attribute_defaults: null, | ||
template_errors: [], | ||
path_dependencies: [], | ||
theme_field_dependencies: [], | ||
template_type_ids: null, | ||
exact_path_references: [], | ||
required_scopes_to_render: [], | ||
}, | ||
renderingErrors: [], | ||
html: '', | ||
}; | ||
|
||
describe('lib/cms/validate', () => { | ||
const accountId = 123; | ||
const filePath = 'test.html'; | ||
|
||
it('should return an empty array if directory has no files', async () => { | ||
mockedFsStat.mockResolvedValue(mockFsStats); | ||
mockedWalk.mockResolvedValue([]); | ||
|
||
const result = await lint(accountId, filePath); | ||
expect(result).toEqual([]); | ||
}); | ||
|
||
it('should return the correct object if a file has no content', async () => { | ||
mockedFsStat.mockResolvedValue({ isDirectory: () => false }); | ||
mockedFsReadFile.mockResolvedValue(' '); | ||
|
||
const result = await lint(accountId, filePath); | ||
expect(result).toEqual([{ file: filePath, validation: null }]); | ||
}); | ||
|
||
it('should call validateHubl with the correct parameters', async () => { | ||
const mockSource = 'valid HUBL content'; | ||
mockedFsStat.mockResolvedValue({ isDirectory: () => false }); | ||
mockedFsReadFile.mockResolvedValue(mockSource); | ||
mockedValidateHubl.mockResolvedValue({ | ||
data: mockValidation, | ||
} as unknown as AxiosPromise<Validation>); | ||
const result = await lint(accountId, filePath); | ||
expect(validateHubl).toHaveBeenCalledWith(accountId, mockSource); | ||
expect(result).toEqual([{ file: filePath, validation: mockValidation }]); | ||
}); | ||
|
||
it('should filter out files with invalid extensions', async () => { | ||
const invalidFile = 'test.txt'; | ||
mockedFsStat.mockResolvedValue({ isDirectory: () => true }); | ||
mockedWalk.mockResolvedValue([invalidFile, filePath]); | ||
mockedFsReadFile.mockResolvedValue('valid HUBL content'); | ||
mockedValidateHubl.mockResolvedValue({ | ||
data: mockValidation, | ||
} as unknown as AxiosPromise<Validation>); | ||
|
||
const result = await lint(accountId, filePath); | ||
|
||
expect(result).toHaveLength(1); | ||
expect((result as Partial<LintResult>[])[0].file).toBe(filePath); | ||
}); | ||
|
||
it('should execute callback if provided', async () => { | ||
const mockCallback = jest.fn(); | ||
const mockSource = 'valid HUBL content'; | ||
mockedFsStat.mockResolvedValue({ isDirectory: () => false }); | ||
mockedFsReadFile.mockResolvedValue(mockSource); | ||
mockedValidateHubl.mockResolvedValue({ | ||
data: mockValidation, | ||
} as unknown as AxiosPromise<Validation>); | ||
|
||
await lint(accountId, filePath, mockCallback); | ||
expect(mockCallback).toHaveBeenCalledWith({ | ||
file: filePath, | ||
validation: mockValidation, | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.