Skip to content

Commit

Permalink
feat: Add new API slug validator; Add/update block category tests;
Browse files Browse the repository at this point in the history
  • Loading branch information
neatbyte-vnobis committed May 22, 2022
1 parent 7a7f98e commit e2746d2
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ describe("Block Categories CRUD Test", () => {

test("create, read, update and delete block categories", async () => {
// Test creating, getting and updating three block categories.
const prefixes = ["block-category-one-", "block-category-two-", "block-category-three-"];
for (let i = 0; i < 3; i++) {
const prefix = `block-category-${i}-`;
const prefix = prefixes[i];
let data = {
slug: `${prefix}slug`,
name: `${prefix}name`
Expand Down Expand Up @@ -83,20 +84,20 @@ describe("Block Categories CRUD Test", () => {
listBlockCategories: {
data: [
{
slug: "block-category-0-slug",
name: "block-category-0-name-UPDATED",
slug: "block-category-one-slug",
name: "block-category-one-name-UPDATED",
createdOn: /^20/,
createdBy: defaultIdentity
},
{
slug: "block-category-1-slug",
name: "block-category-1-name-UPDATED",
slug: "block-category-two-slug",
name: "block-category-two-name-UPDATED",
createdOn: /^20/,
createdBy: defaultIdentity
},
{
slug: "block-category-2-slug",
name: "block-category-2-name-UPDATED",
slug: "block-category-three-slug",
name: "block-category-three-name-UPDATED",
createdOn: /^20/,
createdBy: defaultIdentity
}
Expand All @@ -109,7 +110,7 @@ describe("Block Categories CRUD Test", () => {

// After deleting all block categories, list should be empty.
for (let i = 0; i < 3; i++) {
const prefix = `block-category-${i}-`;
const prefix = prefixes[i];
const data = {
slug: `${prefix}slug`,
name: `${prefix}name-UPDATED`
Expand Down Expand Up @@ -145,4 +146,39 @@ describe("Block Categories CRUD Test", () => {
}
});
});

test("cannot create a block category with invalid slug", async () => {
const [errorResponse] = await createBlockCategory({
data: {
slug: `invalid--slug--category`,
name: `invalid--slug--category`
}
});

const error: ErrorOptions = {
code: "VALIDATION_FAILED_INVALID_FIELDS",
message: "Validation failed.",
data: {
invalidFields: {
slug: {
code: "VALIDATION_FAILED_INVALID_FIELD",
data: null,
message:
"Slug must consist of only 'a-z' and '-' and be max 100 characters long (for example: 'some-entry-slug')"
}
}
}
};

expect(errorResponse).toEqual({
data: {
pageBuilder: {
createBlockCategory: {
data: null,
error
}
}
}
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,37 @@ const NOT_AUTHORIZED_RESPONSE = operation => ({

jest.setTimeout(100000);

const intAsString = [
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten"
];

describe("Block Categories Security Test", () => {
const { createBlockCategory } = useGqlHandler({
permissions: [{ name: "content.i18n" }, { name: "pb.*" }],
identity: identityA
});

test(`"listBlockCategories" only returns entries to which the identity has access to`, async () => {
await createBlockCategory({ data: new Mock("list-block-categories-1-") });
await createBlockCategory({ data: new Mock("list-block-categories-2-") });
await createBlockCategory({ data: new Mock("list-block-categories-one-") });
await createBlockCategory({ data: new Mock("list-block-categories-two-") });

const identityBHandler = useGqlHandler({ identity: identityB });
await identityBHandler.createBlockCategory({ data: new Mock("list-block-categories-3-") });
await identityBHandler.createBlockCategory({ data: new Mock("list-block-categories-4-") });
await identityBHandler.createBlockCategory({
data: new Mock("list-block-categories-three-")
});
await identityBHandler.createBlockCategory({
data: new Mock("list-block-categories-four-")
});

const insufficientPermissions = [
[[], null],
Expand Down Expand Up @@ -83,26 +101,26 @@ describe("Block Categories Security Test", () => {
{
createdBy: identityA,
createdOn: /^20/,
slug: "list-block-categories-1-slug",
name: "list-block-categories-1-name"
slug: "list-block-categories-one-slug",
name: "list-block-categories-one-name"
},
{
createdBy: identityA,
createdOn: /^20/,
slug: "list-block-categories-2-slug",
name: "list-block-categories-2-name"
slug: "list-block-categories-two-slug",
name: "list-block-categories-two-name"
},
{
createdBy: identityB,
createdOn: /^20/,
slug: "list-block-categories-3-slug",
name: "list-block-categories-3-name"
slug: "list-block-categories-three-slug",
name: "list-block-categories-three-name"
},
{
createdBy: identityB,
createdOn: /^20/,
slug: "list-block-categories-4-slug",
name: "list-block-categories-4-name"
slug: "list-block-categories-four-slug",
name: "list-block-categories-four-name"
}
],
error: null
Expand All @@ -126,14 +144,14 @@ describe("Block Categories Security Test", () => {
{
createdBy: identityA,
createdOn: /^20/,
slug: "list-block-categories-1-slug",
name: "list-block-categories-1-name"
slug: "list-block-categories-one-slug",
name: "list-block-categories-one-name"
},
{
createdBy: identityA,
createdOn: /^20/,
slug: "list-block-categories-2-slug",
name: "list-block-categories-2-name"
slug: "list-block-categories-two-slug",
name: "list-block-categories-two-name"
}
],
error: null
Expand All @@ -156,14 +174,14 @@ describe("Block Categories Security Test", () => {
{
createdBy: identityB,
createdOn: /^20/,
slug: "list-block-categories-3-slug",
name: "list-block-categories-3-name"
slug: "list-block-categories-three-slug",
name: "list-block-categories-three-name"
},
{
createdBy: identityB,
createdOn: /^20/,
slug: "list-block-categories-4-slug",
name: "list-block-categories-4-name"
slug: "list-block-categories-four-slug",
name: "list-block-categories-four-name"
}
],
error: null
Expand Down Expand Up @@ -212,7 +230,7 @@ describe("Block Categories Security Test", () => {
identity: identity as any
});

const data = new Mock(`block-category-create-${i}-`);
const data = new Mock(`block-category-create-${intAsString[i]}-`);
const [response] = await createBlockCategory({ data });
expect(response).toMatchObject({
data: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import WebinyError from "@webiny/error";
import { createTopic } from "@webiny/pubsub";

const CreateDataModel = withFields({
slug: string({ validation: validation.create("required,minLength:1,maxLength:100") }),
slug: string({ validation: validation.create("required,slug") }),
name: string({ validation: validation.create("required,minLength:1,maxLength:100") })
})();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
export const blockCategorySlugValidator = (value: string): boolean => {
if (value.match(/^[a-z]+(\-[a-z]+)*$/)) {
return true;
if (!value.match(/^[a-z]+(\-[a-z]+)*$/)) {
throw new Error(
"Block Category slug must consist of only 'a-z' and '-' characters (for example: 'block-category-slug')"
);
}

throw new Error(
"Block Category slug must consist of only 'a-z' and '-' characters (for example: 'block-category-slug')"
);
if (value.length > 100) {
throw new Error("Block Category slug must shorter than 100 characters");
}

return true;
};
71 changes: 71 additions & 0 deletions packages/validation/__tests__/slug.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { validation, ValidationError } from "../src";

describe("slug test", () => {
it("should not get triggered if empty value was set", async () => {
await expect(validation.validate(null, "slug")).resolves.toBe(true);
});

it("should not get triggered if correct value was set", async () => {
//await expect(validation.validate("test-slug-correct", "slug")).resolves.toBe(true);
await expect(validation.validate("test-slug", "slug")).resolves.toBe(true);
await expect(validation.validate("test", "slug")).resolves.toBe(true);
});

it("should fail - wrong dash character usage", async () => {
await expect(validation.validate("test--slug", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("test---slug", "slug")).rejects.toThrow(ValidationError);

await expect(validation.validate("-test-slug", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("test-slug-", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("--slug--", "slug")).rejects.toThrow(ValidationError);

await expect(validation.validate("-slug-", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("-slug", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("slug-", "slug")).rejects.toThrow(ValidationError);
});

it("should fail - uppercase letters are not allowed", async () => {
await expect(validation.validate("Test-Slug", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("test-Slug", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("Test-slug", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("tesT-sluG", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("Test", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("tEst", "slug")).rejects.toThrow(ValidationError);
});

it("should fail - numbers are not allowed", async () => {
await expect(validation.validate("test-slug-12345", "slug")).rejects.toThrow(
ValidationError
);
await expect(validation.validate("test-12345-slug", "slug")).rejects.toThrow(
ValidationError
);
await expect(validation.validate("12345-test-slug", "slug")).rejects.toThrow(
ValidationError
);
await expect(validation.validate("test123-slug", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("test-slug123", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("12345-slug", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("slug-12345", "slug")).rejects.toThrow(ValidationError);

await expect(validation.validate("slug12345", "slug")).rejects.toThrow(ValidationError);
});

it("should fail - special chars are not allowed", async () => {
await expect(validation.validate("test-slug-&%^#", "slug")).rejects.toThrow(
ValidationError
);

await expect(validation.validate("test-&%^#-slug", "slug")).rejects.toThrow(
ValidationError
);
await expect(validation.validate("&%^#-test-slug", "slug")).rejects.toThrow(
ValidationError
);

await expect(validation.validate("&%^#-test", "slug")).rejects.toThrow(ValidationError);
await expect(validation.validate("test-&%^#", "slug")).rejects.toThrow(ValidationError);

await expect(validation.validate("test&%^#", "slug")).rejects.toThrow(ValidationError);
});
});
2 changes: 2 additions & 0 deletions packages/validation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import dateGte from "./validators/dateGte";
import dateLte from "./validators/dateLte";
import timeGte from "./validators/timeGte";
import timeLte from "./validators/timeLte";
import slug from "./validators/slug";

const validation = new Validation();
validation.setValidator("creditCard", creditCard);
Expand All @@ -46,5 +47,6 @@ validation.setValidator("dateGte", dateGte);
validation.setValidator("dateLte", dateLte);
validation.setValidator("timeGte", timeGte);
validation.setValidator("timeLte", timeLte);
validation.setValidator("slug", slug);

export { validation, Validation, ValidationError };
16 changes: 16 additions & 0 deletions packages/validation/src/validators/slug.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ValidationError from "~/validationError";

export default (value: any) => {
if (!value) {
return;
}
value = value + "";

if (value.match(/^[a-z]+(\-[a-z]+)*$/) && value.length <= 100) {
return;
}

throw new ValidationError(
"Slug must consist of only 'a-z' and '-' and be max 100 characters long (for example: 'some-entry-slug')"
);
};

0 comments on commit e2746d2

Please sign in to comment.