diff --git a/webapp/components/utils/UniqueSlugForm.spec.js b/webapp/components/utils/UniqueSlugForm.spec.js new file mode 100644 index 000000000..de0e3fee6 --- /dev/null +++ b/webapp/components/utils/UniqueSlugForm.spec.js @@ -0,0 +1,80 @@ +import UniqueSlugForm from './UniqueSlugForm' +import Schema from 'async-validator' + +let translate +let apollo +let currentUser + +beforeEach(() => { + translate = jest.fn(() => 'Validation error') + apollo = { + query: jest.fn().mockResolvedValue({ data: { User: [] } }), + } + currentUser = null +}) + +describe('UniqueSlugForm', () => { + let validate = object => { + const { formSchema } = UniqueSlugForm({ translate, apollo, currentUser }) + const validator = new Schema(formSchema) + return validator.validate(object, { suppressWarning: true }).catch(({ errors }) => { + throw new Error(errors[0].message) + }) + } + + describe('regex', () => { + describe('non URL-safe characters, e.g. whitespaces', () => { + it('rejects', async () => { + await expect(validate({ slug: 'uh oh' })).rejects.toThrow('Validation error') + }) + }) + + describe('alphanumeric, hyphens or underscores', () => { + it('validates', async () => { + await expect(validate({ slug: '_all-right_' })).resolves.toBeUndefined() + }) + }) + }) + + describe('given a currentUser with a slug', () => { + beforeEach(() => { + currentUser = { slug: 'current-user' } + }) + + describe('backend returns no user for given slug', () => { + beforeEach(() => { + apollo.query.mockResolvedValue({ + data: { User: [] }, + }) + }) + + it('validates', async () => { + await expect(validate({ slug: 'slug' })).resolves.toBeUndefined() + }) + }) + + describe('backend returns user', () => { + let slug + beforeEach(() => { + slug = 'already-taken' + apollo.query.mockResolvedValue({ + data: { User: [{ slug: 'already-taken' }] }, + }) + }) + + it('rejects', async () => { + await expect(validate({ slug: 'uh oh' })).rejects.toThrow('Validation error') + }) + + describe('but it is the current user', () => { + beforeEach(() => { + currentUser = { slug: 'already-taken' } + }) + + it('validates', async () => { + await expect(validate({ slug })).resolves.toBeUndefined() + }) + }) + }) + }) +}) diff --git a/webapp/package.json b/webapp/package.json index 598412568..f18556e99 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -97,6 +97,7 @@ "@vue/eslint-config-prettier": "~5.0.0", "@vue/server-test-utils": "~1.0.0-beta.29", "@vue/test-utils": "~1.0.0-beta.29", + "async-validator": "^3.1.0", "babel-core": "~7.0.0-bridge.0", "babel-eslint": "~10.0.3", "babel-jest": "~24.9.0", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 6fdcc9134..a86ae8527 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -3649,6 +3649,11 @@ async-retry@^1.2.1: dependencies: retry "0.12.0" +async-validator@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-3.1.0.tgz#447db5eb003cbb47e650f040037a29fc3881ce92" + integrity sha512-XyAHGwtpx3Y3aHIOaGXXFo4tiulnrh+mXBU9INxig6Q8rtmtmBxDuCxb60j7EIGbAsQg9cxfJ2jrUZ+fIqEnBQ== + async@^2.1.4: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381"