diff options
Diffstat (limited to 'spec/frontend/editor')
9 files changed, 303 insertions, 0 deletions
diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js index 0f380f13679..7986509074e 100644 --- a/spec/frontend/editor/schema/ci/ci_schema_spec.js +++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js @@ -23,6 +23,7 @@ import RetryUnknownWhenJson from './json_tests/negative_tests/retry_unknown_when // YAML POSITIVE TEST import ArtifactsYaml from './yaml_tests/positive_tests/artifacts.yml'; +import ImageYaml from './yaml_tests/positive_tests/image.yml'; import CacheYaml from './yaml_tests/positive_tests/cache.yml'; import FilterYaml from './yaml_tests/positive_tests/filter.yml'; import IncludeYaml from './yaml_tests/positive_tests/include.yml'; @@ -37,9 +38,12 @@ import SecretsYaml from './yaml_tests/positive_tests/secrets.yml'; import ServicesYaml from './yaml_tests/positive_tests/services.yml'; import NeedsParallelMatrixYaml from './yaml_tests/positive_tests/needs_parallel_matrix.yml'; import ScriptYaml from './yaml_tests/positive_tests/script.yml'; +import AutoCancelPipelineOnJobFailureAllYaml from './yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/all.yml'; +import AutoCancelPipelineOnJobFailureNoneYaml from './yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/none.yml'; // YAML NEGATIVE TEST import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml'; +import ImageNegativeYaml from './yaml_tests/negative_tests/image.yml'; import CacheKeyNeative from './yaml_tests/negative_tests/cache.yml'; import IncludeNegativeYaml from './yaml_tests/negative_tests/include.yml'; import JobWhenNegativeYaml from './yaml_tests/negative_tests/job_when.yml'; @@ -62,6 +66,7 @@ import NeedsParallelMatrixNumericYaml from './yaml_tests/negative_tests/needs/pa import NeedsParallelMatrixWrongParallelValueYaml from './yaml_tests/negative_tests/needs/parallel_matrix/wrong_parallel_value.yml'; import NeedsParallelMatrixWrongMatrixValueYaml from './yaml_tests/negative_tests/needs/parallel_matrix/wrong_matrix_value.yml'; import ScriptNegativeYaml from './yaml_tests/negative_tests/script.yml'; +import AutoCancelPipelineNegativeYaml from './yaml_tests/negative_tests/auto_cancel_pipeline.yml'; const ajv = new Ajv({ strictTypes: false, @@ -90,6 +95,7 @@ describe('positive tests', () => { // YAML ArtifactsYaml, + ImageYaml, CacheYaml, FilterYaml, IncludeYaml, @@ -104,6 +110,8 @@ describe('positive tests', () => { SecretsYaml, NeedsParallelMatrixYaml, ScriptYaml, + AutoCancelPipelineOnJobFailureAllYaml, + AutoCancelPipelineOnJobFailureNoneYaml, }), )('schema validates %s', (_, input) => { // We construct a new "JSON" from each main key that is inside a @@ -126,6 +134,7 @@ describe('negative tests', () => { // YAML ArtifactsNegativeYaml, + ImageNegativeYaml, CacheKeyNeative, HooksNegative, IdTokensNegativeYaml, @@ -148,6 +157,7 @@ describe('negative tests', () => { NeedsParallelMatrixWrongParallelValueYaml, NeedsParallelMatrixWrongMatrixValueYaml, ScriptNegativeYaml, + AutoCancelPipelineNegativeYaml, }), )('schema validates %s', (_, input) => { // We construct a new "JSON" from each main key that is inside a diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/auto_cancel_pipeline.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/auto_cancel_pipeline.yml new file mode 100644 index 00000000000..0ba3e5632e3 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/auto_cancel_pipeline.yml @@ -0,0 +1,4 @@ +# invalid workflow:auto-cancel:on-job-failure +workflow: + auto_cancel: + on_job_failure: unexpected_value diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/image.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/image.yml new file mode 100644 index 00000000000..ad37cd6c3ba --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/image.yml @@ -0,0 +1,38 @@ +empty_image: + image: + +multi_image_array: + image: + - alpine:latest + - ubuntu:latest + +image_without_name: + image: + entrypoint: ["/bin/sh", "-c"] + +image_with_invalid_entrypoint: + image: + name: my-postgres:11.7 + entrypoint: "/usr/local/bin/db-postgres" # must be array + +image_with_empty_pull_policy: + image: + name: postgres:11.6 + pull_policy: [] + +invalid_image_platform: + image: + name: alpine:latest + docker: + platform: ["arm64"] # The expected value is a string, not an array + +invalid_image_executor_opts: + image: + name: alpine:latest + docker: + unknown_key: test + +image_with_empty_executor_opts: + image: + name: alpine:latest + docker: diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/services.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/services.yml index 6761a603a0a..e14ac9ca86e 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/services.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/services.yml @@ -36,3 +36,17 @@ empty_pull_policy: services: - name: postgres:11.6 pull_policy: [] + +invalid_service_executor_opts: + script: echo "Specifying platform." + services: + - name: mysql:5.7 + docker: + unknown_key: test + +invalid_service_platform: + script: echo "Specifying platform." + services: + - name: mysql:5.7 + docker: + platform: ["arm64"] # The expected value is a string, not an array diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/all.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/all.yml new file mode 100644 index 00000000000..bf84ff16f42 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/all.yml @@ -0,0 +1,4 @@ +# valid workflow:auto-cancel:on-job-failure +workflow: + auto_cancel: + on_job_failure: all diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/none.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/none.yml new file mode 100644 index 00000000000..b99eb50e962 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/auto_cancel_pipeline/on_job_failure/none.yml @@ -0,0 +1,4 @@ +# valid workflow:auto-cancel:on-job-failure +workflow: + auto_cancel: + on_job_failure: none diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/image.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/image.yml new file mode 100644 index 00000000000..4c2559d0800 --- /dev/null +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/image.yml @@ -0,0 +1,41 @@ +valid_image: + image: alpine:latest + +valid_image_basic: + image: + name: alpine:latest + +valid_image_with_entrypoint: + image: + name: alpine:latest + entrypoint: + - /bin/sh + - -c + +valid_image_with_pull_policy: + image: + name: alpine:latest + pull_policy: always + +valid_image_with_pull_policies: + image: + name: alpine:latest + pull_policy: + - always + - if-not-present + +valid_image_with_docker: + image: + name: alpine:latest + docker: + platform: linux/amd64 + +valid_image_full: + image: + name: alpine:latest + entrypoint: + - /bin/sh + - -c + docker: + platform: linux/amd64 + pull_policy: if-not-present diff --git a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/services.yml b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/services.yml index 8a0f59d1dfd..1d19ee52cc3 100644 --- a/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/services.yml +++ b/spec/frontend/editor/schema/ci/yaml_tests/positive_tests/services.yml @@ -29,3 +29,10 @@ pull_policy_array: services: - name: postgres:11.6 pull_policy: [always, if-not-present] + +services_platform_string: + script: echo "Specifying platform." + services: + - name: mysql:5.7 + docker: + platform: arm64 diff --git a/spec/frontend/editor/source_editor_security_policy_schema_ext_spec.js b/spec/frontend/editor/source_editor_security_policy_schema_ext_spec.js new file mode 100644 index 00000000000..96c876b27c9 --- /dev/null +++ b/spec/frontend/editor/source_editor_security_policy_schema_ext_spec.js @@ -0,0 +1,181 @@ +import MockAdapter from 'axios-mock-adapter'; +import { registerSchema } from '~/ide/utils'; +import axios from '~/lib/utils/axios_utils'; +import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; +import { TEST_HOST } from 'helpers/test_constants'; +import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; +import { + getSecurityPolicyListUrl, + getSecurityPolicySchemaUrl, + getSinglePolicySchema, + SecurityPolicySchemaExtension, +} from '~/editor/extensions/source_editor_security_policy_schema_ext'; +import SourceEditor from '~/editor/source_editor'; + +jest.mock('~/ide/utils'); + +const mockNamespacePath = 'mock-namespace'; + +const mockSchema = { + $id: 1, + title: 'mockSchema', + description: 'mockDescriptions', + type: 'Object', + properties: { + scan_execution_policy: { items: { properties: { foo: 'bar' } } }, + scan_result_policy: { items: { properties: { fizz: 'buzz' } } }, + }, +}; + +const createMockOutput = (policyType) => ({ + $id: mockSchema.$id, + title: mockSchema.title, + description: mockSchema.description, + type: mockSchema.type, + properties: { + type: { + type: 'string', + description: 'Specifies the type of policy to be enforced.', + enum: policyType, + }, + ...mockSchema.properties[policyType].items.properties, + }, +}); + +describe('getSecurityPolicyListUrl', () => { + it.each` + input | output + ${{ namespacePath: '' }} | ${`${TEST_HOST}/groups/-/security/policies`} + ${{ namespacePath: 'test', namespaceType: 'group' }} | ${`${TEST_HOST}/groups/test/-/security/policies`} + ${{ namespacePath: '', namespaceType: 'project' }} | ${`${TEST_HOST}/-/security/policies`} + ${{ namespacePath: 'test', namespaceType: 'project' }} | ${`${TEST_HOST}/test/-/security/policies`} + ${{ namespacePath: undefined, namespaceType: 'project' }} | ${`${TEST_HOST}/-/security/policies`} + ${{ namespacePath: undefined, namespaceType: 'group' }} | ${`${TEST_HOST}/groups/-/security/policies`} + ${{ namespacePath: null, namespaceType: 'project' }} | ${`${TEST_HOST}/-/security/policies`} + ${{ namespacePath: null, namespaceType: 'group' }} | ${`${TEST_HOST}/groups/-/security/policies`} + `('returns `$output` when passed `$input`', ({ input, output }) => { + expect(getSecurityPolicyListUrl(input)).toBe(output); + }); +}); + +describe('getSecurityPolicySchemaUrl', () => { + it.each` + namespacePath | namespaceType | output + ${'test'} | ${'project'} | ${`${TEST_HOST}/test/-/security/policies/schema`} + ${'test'} | ${'group'} | ${`${TEST_HOST}/groups/test/-/security/policies/schema`} + `( + 'returns $output when passed $namespacePath and $namespaceType', + ({ namespacePath, namespaceType, output }) => { + expect(getSecurityPolicySchemaUrl({ namespacePath, namespaceType })).toBe(output); + }, + ); +}); + +describe('getSinglePolicySchema', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + }); + + afterEach(() => { + mock.restore(); + }); + + it.each` + policyType + ${'scan_execution_policy'} + ${'scan_result_policy'} + `('returns the appropriate schema on request success for $policyType', async ({ policyType }) => { + mock.onGet().reply(HTTP_STATUS_OK, mockSchema); + + await expect( + getSinglePolicySchema({ + namespacePath: mockNamespacePath, + namespaceType: 'project', + policyType, + }), + ).resolves.toStrictEqual(createMockOutput(policyType)); + }); + + it('returns an empty schema on request failure', async () => { + await expect( + getSinglePolicySchema({ + namespacePath: mockNamespacePath, + namespaceType: 'project', + policyType: 'scan_execution_policy', + }), + ).resolves.toStrictEqual({}); + }); + + it('returns an empty schema on non-existing policy type', async () => { + await expect( + getSinglePolicySchema({ + namespacePath: mockNamespacePath, + namespaceType: 'project', + policyType: 'non_existent_policy', + }), + ).resolves.toStrictEqual({}); + }); +}); + +describe('SecurityPolicySchemaExtension', () => { + let mock; + let editor; + let instance; + let editorEl; + + const createMockEditor = ({ blobPath = '.gitlab/security-policies/policy.yml' } = {}) => { + setHTMLFixture('<div id="editor"></div>'); + editorEl = document.getElementById('editor'); + editor = new SourceEditor(); + instance = editor.createInstance({ el: editorEl, blobPath, blobContent: '' }); + instance.use({ definition: SecurityPolicySchemaExtension }); + }; + + beforeEach(() => { + createMockEditor(); + mock = new MockAdapter(axios); + mock.onGet().reply(HTTP_STATUS_OK, mockSchema); + }); + + afterEach(() => { + instance.dispose(); + editorEl.remove(); + resetHTMLFixture(); + mock.restore(); + }); + + describe('registerSecurityPolicyEditorSchema', () => { + describe('register validations options with monaco for yaml language', () => { + it('registers the schema', async () => { + const policyType = 'scan_execution_policy'; + await instance.registerSecurityPolicyEditorSchema({ + namespacePath: mockNamespacePath, + namespaceType: 'project', + policyType, + }); + + expect(registerSchema).toHaveBeenCalledTimes(1); + expect(registerSchema).toHaveBeenCalledWith({ + uri: `${TEST_HOST}/${mockNamespacePath}/-/security/policies/schema`, + schema: createMockOutput(policyType), + fileMatch: ['policy.yml'], + }); + }); + }); + }); + + describe('registerSecurityPolicySchema', () => { + describe('register validations options with monaco for yaml language', () => { + it('registers the schema', async () => { + await instance.registerSecurityPolicySchema(mockNamespacePath); + expect(registerSchema).toHaveBeenCalledTimes(1); + expect(registerSchema).toHaveBeenCalledWith({ + uri: `${TEST_HOST}/${mockNamespacePath}/-/security/policies/schema`, + fileMatch: ['policy.yml'], + }); + }); + }); + }); +}); |