mirror of https://github.com/nocodb/nocodb
Daniel Spaude
2 years ago
41 changed files with 1133 additions and 64 deletions
@ -0,0 +1,120 @@
|
||||
<script setup lang="ts"> |
||||
import type { UITypes } from 'nocodb-sdk' |
||||
import { AllowedColumnTypesForQrAndBarcodes } from 'nocodb-sdk' |
||||
import type { SelectProps } from 'ant-design-vue' |
||||
import { onMounted, useVModel, watch } from '#imports' |
||||
|
||||
const props = defineProps<{ |
||||
modelValue: any |
||||
}>() |
||||
|
||||
const emit = defineEmits(['update:modelValue']) |
||||
|
||||
const meta = inject(MetaInj, ref()) |
||||
|
||||
const activeView = inject(ActiveViewInj, ref()) |
||||
|
||||
const reloadDataHook = inject(ReloadViewDataHookInj)! |
||||
|
||||
const { fields, metaColumnById } = useViewColumns(activeView, meta, () => reloadDataHook.trigger()) |
||||
|
||||
const vModel = useVModel(props, 'modelValue', emit) |
||||
|
||||
const { setAdditionalValidations, validateInfos, column } = useColumnCreateStoreOrThrow() |
||||
|
||||
const columnsAllowedAsBarcodeValue = computed<SelectProps['options']>(() => { |
||||
return fields.value |
||||
?.filter( |
||||
(el) => |
||||
el.fk_column_id && AllowedColumnTypesForQrAndBarcodes.includes(metaColumnById.value[el.fk_column_id].uidt as UITypes), |
||||
) |
||||
.map((field) => { |
||||
return { |
||||
value: field.fk_column_id, |
||||
label: field.title, |
||||
} |
||||
}) |
||||
}) |
||||
|
||||
const supportedBarcodeFormats = [ |
||||
{ value: 'CODE128', label: 'CODE128' }, |
||||
{ value: 'upc', label: 'UPC' }, |
||||
{ value: 'EAN13', label: 'EAN-13' }, |
||||
{ value: 'EAN8', label: 'EAN-8' }, |
||||
{ value: 'EAN5', label: 'EAN-5' }, |
||||
{ value: 'EAN2', label: 'EAN-2' }, |
||||
{ value: 'CODE39', label: 'CODE39' }, |
||||
{ value: 'ITF14', label: 'ITF-14' }, |
||||
{ value: 'MSI', label: 'MSI' }, |
||||
{ value: 'PHARMACODE', label: 'pharmacode' }, |
||||
{ value: 'CODABAR', label: 'codabar' }, |
||||
] |
||||
|
||||
onMounted(() => { |
||||
// set default value |
||||
vModel.value.meta = { |
||||
barcodeFormat: supportedBarcodeFormats[0].value, |
||||
...vModel.value.meta, |
||||
} |
||||
vModel.value.fk_barcode_value_column_id = |
||||
(column?.value?.colOptions as Record<string, any>)?.fk_barcode_value_column_id || columnsAllowedAsBarcodeValue.value?.[0] |
||||
}) |
||||
|
||||
watch(columnsAllowedAsBarcodeValue, (newColumnsAllowedAsBarcodeValue) => { |
||||
if (vModel.value.fk_barcode_value_column_id == null) { |
||||
vModel.value.fk_barcode_value_column_id = newColumnsAllowedAsBarcodeValue?.[0]?.value |
||||
} |
||||
}) |
||||
|
||||
setAdditionalValidations({ |
||||
fk_barcode_value_column_id: [{ required: true, message: 'Required' }], |
||||
barcode_format: [{ required: true, message: 'Required' }], |
||||
}) |
||||
|
||||
const showBarcodeValueColumnInfoIcon = computed(() => !columnsAllowedAsBarcodeValue.value?.length) |
||||
</script> |
||||
|
||||
<template> |
||||
<a-row> |
||||
<a-col :span="24"> |
||||
<a-form-item |
||||
class="flex pb-2 nc-barcode-value-column-select flex-row" |
||||
:label="$t('labels.barcodeValueColumn')" |
||||
v-bind="validateInfos.fk_barcode_value_column_id" |
||||
> |
||||
<div class="flex w-1/2 flex-row items-center"> |
||||
<a-select |
||||
v-model:value="vModel.fk_barcode_value_column_id" |
||||
:options="columnsAllowedAsBarcodeValue" |
||||
placeholder="Select a column for the Barcode value" |
||||
not-found-content="No valid Column Type can be found." |
||||
@click.stop |
||||
/> |
||||
<div v-if="showBarcodeValueColumnInfoIcon" class="pl-2"> |
||||
<a-tooltip placement="bottom"> |
||||
<template #title> |
||||
<span> |
||||
The valid Column Types for a Barcode Column are: Number, Single Line Text, Long Text, Phone Number, URL, Email, |
||||
Decimal. Please create one first. |
||||
</span> |
||||
</template> |
||||
<mdi-information class="cursor-pointer" /> |
||||
</a-tooltip> |
||||
</div> |
||||
</div> |
||||
</a-form-item> |
||||
<a-form-item |
||||
class="flex w-1/2 pb-2 nc-barcode-format-select" |
||||
:label="$t('labels.barcodeFormat')" |
||||
v-bind="validateInfos.barcode_format" |
||||
> |
||||
<a-select |
||||
v-model:value="vModel.meta.barcodeFormat" |
||||
:options="supportedBarcodeFormats" |
||||
placeholder="Select a Barcode format" |
||||
@click.stop |
||||
/> |
||||
</a-form-item> |
||||
</a-col> |
||||
</a-row> |
||||
</template> |
@ -0,0 +1,68 @@
|
||||
<script setup lang="ts"> |
||||
import JsBarcodeWrapper from './JsBarcodeWrapper.vue' |
||||
|
||||
const maxNumberOfAllowedCharsForBarcodeValue = 100 |
||||
|
||||
const cellValue = inject(CellValueInj) |
||||
|
||||
const column = inject(ColumnInj) |
||||
|
||||
const barcodeValue: ComputedRef<string> = computed(() => String(cellValue?.value || '')) |
||||
|
||||
const tooManyCharsForBarcode = computed(() => barcodeValue.value.length > maxNumberOfAllowedCharsForBarcodeValue) |
||||
|
||||
const modalVisible = ref(false) |
||||
|
||||
const showBarcodeModal = () => { |
||||
modalVisible.value = true |
||||
} |
||||
|
||||
const barcodeMeta = $computed(() => { |
||||
return { |
||||
barcodeFormat: 'CODE128', |
||||
...(column?.value?.meta || {}), |
||||
} |
||||
}) |
||||
|
||||
const handleModalOkClick = () => (modalVisible.value = false) |
||||
|
||||
const showBarcode = computed(() => barcodeValue?.value.length > 0 && !tooManyCharsForBarcode.value) |
||||
|
||||
const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning } = useShowNotEditableWarning() |
||||
</script> |
||||
|
||||
<template> |
||||
<a-modal |
||||
v-model:visible="modalVisible" |
||||
:class="{ active: modalVisible }" |
||||
wrap-class-name="nc-barcode-large" |
||||
:body-style="{ padding: '0px' }" |
||||
:footer="null" |
||||
@ok="handleModalOkClick" |
||||
> |
||||
<JsBarcodeWrapper v-if="showBarcode" :barcode-value="barcodeValue" :barcode-format="barcodeMeta.barcodeFormat" /> |
||||
</a-modal> |
||||
<JsBarcodeWrapper |
||||
v-if="showBarcode" |
||||
:barcode-value="barcodeValue" |
||||
:barcode-format="barcodeMeta.barcodeFormat" |
||||
class="nc-barcode-svg" |
||||
@on-click-barcode="showBarcodeModal" |
||||
> |
||||
<template #barcodeRenderError> |
||||
<div class="text-left text-wrap mt-2 text-[#e65100] text-xs" data-testid="barcode-invalid-input-message"> |
||||
{{ $t('msg.warning.barcode.renderError') }} |
||||
</div> |
||||
</template> |
||||
</JsBarcodeWrapper> |
||||
|
||||
<div v-if="tooManyCharsForBarcode" class="text-left text-wrap mt-2 text-[#e65100] text-xs"> |
||||
{{ $t('labels.barcodeValueTooLong') }} |
||||
</div> |
||||
<div v-if="showEditNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs"> |
||||
{{ $t('msg.warning.nonEditableFields.computedFieldUnableToClear') }} |
||||
</div> |
||||
<div v-if="showClearNonEditableFieldWarning" class="text-left text-wrap mt-2 text-[#e65100] text-xs"> |
||||
{{ $t('msg.warning.nonEditableFields.barcodeFieldsCannotBeDirectlyChanged') }} |
||||
</div> |
||||
</template> |
@ -0,0 +1,39 @@
|
||||
<script lang="ts" setup> |
||||
import JsBarcode from 'jsbarcode' |
||||
import { onMounted } from '#imports' |
||||
|
||||
const props = defineProps({ |
||||
barcodeValue: { type: String, required: true }, |
||||
barcodeFormat: { type: String, required: true }, |
||||
}) |
||||
|
||||
const emit = defineEmits(['onClickBarcode']) |
||||
|
||||
const barcodeSvgRef = ref(null) |
||||
const errorForCurrentInput = ref(false) |
||||
|
||||
const generate = () => { |
||||
try { |
||||
JsBarcode(barcodeSvgRef.value, String(props.barcodeValue), { |
||||
format: props.barcodeFormat, |
||||
}) |
||||
errorForCurrentInput.value = false |
||||
} catch (e) { |
||||
console.log('e', e) |
||||
errorForCurrentInput.value = true |
||||
} |
||||
} |
||||
|
||||
const onBarcodeClick = (ev: MouseEvent) => { |
||||
ev.stopPropagation() |
||||
emit('onClickBarcode') |
||||
} |
||||
|
||||
watch([() => props.barcodeValue, () => props.barcodeFormat], generate) |
||||
onMounted(generate) |
||||
</script> |
||||
|
||||
<template> |
||||
<svg v-show="!errorForCurrentInput" ref="barcodeSvgRef" class="w-full" data-testid="barcode" @click="onBarcodeClick"></svg> |
||||
<slot v-if="errorForCurrentInput" name="barcodeRenderError" /> |
||||
</template> |
@ -1,6 +1,6 @@
|
||||
import UITypes from '../UITypes'; |
||||
|
||||
export const AllowedColumnTypesForQrCode = [ |
||||
export const AllowedColumnTypesForQrAndBarcodes = [ |
||||
UITypes.Formula, |
||||
UITypes.SingleLineText, |
||||
UITypes.LongText, |
@ -1 +1 @@
|
||||
export * from './QrCodeRules'; |
||||
export * from './QrAndBarcodeRules'; |
||||
|
@ -0,0 +1,26 @@
|
||||
import { MetaTable } from '../../utils/globals'; |
||||
import { Knex } from 'knex'; |
||||
|
||||
const up = async (knex: Knex) => { |
||||
await knex.schema.createTable(MetaTable.COL_BARCODE, (table) => { |
||||
table.string('id', 20).primary().notNullable(); |
||||
|
||||
table.string('fk_column_id', 20); |
||||
table.foreign('fk_column_id').references(`${MetaTable.COLUMNS}.id`); |
||||
|
||||
table.string('fk_barcode_value_column_id', 20); |
||||
table |
||||
.foreign('fk_barcode_value_column_id') |
||||
.references(`${MetaTable.COLUMNS}.id`); |
||||
|
||||
table.string('barcode_format', 15); |
||||
table.boolean('deleted'); |
||||
table.timestamps(true, true); |
||||
}); |
||||
}; |
||||
|
||||
const down = async (knex: Knex) => { |
||||
await knex.schema.dropTable(MetaTable.COL_BARCODE); |
||||
}; |
||||
|
||||
export { up, down }; |
@ -0,0 +1,69 @@
|
||||
import Noco from '../Noco'; |
||||
import { CacheGetType, CacheScope, MetaTable } from '../utils/globals'; |
||||
import NocoCache from '../cache/NocoCache'; |
||||
import { extractProps } from '../meta/helpers/extractProps'; |
||||
|
||||
export default class BarcodeColumn { |
||||
id: string; |
||||
fk_column_id: string; |
||||
fk_barcode_value_column_id: string; |
||||
barcode_format: string; |
||||
|
||||
constructor(data: Partial<BarcodeColumn>) { |
||||
Object.assign(this, data); |
||||
} |
||||
|
||||
public static async insert( |
||||
data: Partial<BarcodeColumn>, |
||||
ncMeta = Noco.ncMeta |
||||
) { |
||||
await ncMeta.metaInsert2(null, null, MetaTable.COL_BARCODE, { |
||||
fk_column_id: data.fk_column_id, |
||||
fk_barcode_value_column_id: data.fk_barcode_value_column_id, |
||||
barcode_format: data.barcode_format, |
||||
}); |
||||
|
||||
return this.read(data.fk_column_id, ncMeta); |
||||
} |
||||
public static async read(columnId: string, ncMeta = Noco.ncMeta) { |
||||
let column = |
||||
columnId && |
||||
(await NocoCache.get( |
||||
`${CacheScope.COL_BARCODE}:${columnId}`, |
||||
CacheGetType.TYPE_OBJECT |
||||
)); |
||||
if (!column) { |
||||
column = await ncMeta.metaGet2( |
||||
null, //,
|
||||
null, //model.db_alias,
|
||||
MetaTable.COL_BARCODE, |
||||
{ fk_column_id: columnId } |
||||
); |
||||
await NocoCache.set(`${CacheScope.COL_BARCODE}:${columnId}`, column); |
||||
} |
||||
|
||||
return column ? new BarcodeColumn(column) : null; |
||||
} |
||||
|
||||
static async update( |
||||
id: string, |
||||
barcode: Partial<BarcodeColumn>, |
||||
ncMeta = Noco.ncMeta |
||||
) { |
||||
const updateObj = extractProps(barcode, [ |
||||
'fk_column_id', |
||||
'fk_barcode_value_column_id', |
||||
'barcode_format', |
||||
]); |
||||
// get existing cache
|
||||
const key = `${CacheScope.COL_BARCODE}:${id}`; |
||||
let o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT); |
||||
if (o) { |
||||
o = { ...o, ...updateObj }; |
||||
// set cache
|
||||
await NocoCache.set(key, o); |
||||
} |
||||
// set meta
|
||||
await ncMeta.metaUpdate(null, null, MetaTable.COL_BARCODE, updateObj, id); |
||||
} |
||||
} |
@ -0,0 +1,176 @@
|
||||
import { expect } from 'chai' |
||||
import fs from 'fs' |
||||
import { OrgUserRoles, ProjectRoles } from 'nocodb-sdk' |
||||
import path from 'path' |
||||
import 'mocha' |
||||
import request from 'supertest' |
||||
import { createProject } from '../../factory/project' |
||||
import init from '../../init' |
||||
|
||||
const FILE_PATH = path.join(__dirname, 'test.txt') |
||||
|
||||
function attachmentTests() { |
||||
let context |
||||
|
||||
|
||||
beforeEach(async function() { |
||||
context = await init() |
||||
fs.writeFileSync(FILE_PATH, 'test', `utf-8`) |
||||
context = await init() |
||||
}) |
||||
|
||||
|
||||
afterEach(function() { |
||||
fs.unlinkSync(FILE_PATH) |
||||
}) |
||||
|
||||
|
||||
it('Upload file - Super admin', async () => { |
||||
const response = await request(context.app) |
||||
.post('/api/v1/db/storage/upload') |
||||
.attach('files', FILE_PATH) |
||||
.set('xc-auth', context.token) |
||||
.expect(200) |
||||
|
||||
|
||||
const attachments = response.body |
||||
expect(attachments).to.be.an('array') |
||||
expect(attachments[0].title).to.be.eq(path.basename(FILE_PATH)) |
||||
}) |
||||
|
||||
it('Upload file - Without token', async () => { |
||||
const response = await request(context.app) |
||||
.post('/api/v1/db/storage/upload') |
||||
.attach('files', FILE_PATH) |
||||
.expect(401) |
||||
|
||||
const msg = response.body.msg |
||||
expect(msg).to.be.eq('Unauthorized') |
||||
}) |
||||
|
||||
it('Upload file - Org level viewer', async () => { |
||||
|
||||
// signup a user
|
||||
const args = { |
||||
email: 'dummyuser@example.com', |
||||
password: 'A1234abh2@dsad', |
||||
} |
||||
|
||||
const signupResponse = await request(context.app) |
||||
.post('/api/v1/auth/user/signup') |
||||
.send(args) |
||||
.expect(200) |
||||
|
||||
const response = await request(context.app) |
||||
.post('/api/v1/db/storage/upload') |
||||
.attach('files', FILE_PATH) |
||||
.set('xc-auth', signupResponse.body.token) |
||||
.expect(400) |
||||
|
||||
const msg = response.body.msg |
||||
expect(msg).to.be.eq('Upload not allowed') |
||||
}) |
||||
|
||||
|
||||
it('Upload file - Org level creator', async () => { |
||||
|
||||
// signup a user
|
||||
const args = { |
||||
email: 'dummyuser@example.com', |
||||
password: 'A1234abh2@dsad', |
||||
} |
||||
|
||||
|
||||
await request(context.app) |
||||
.post('/api/v1/auth/user/signup') |
||||
.send(args) |
||||
.expect(200) |
||||
|
||||
// update user role to creator
|
||||
const usersListResponse = await request(context.app) |
||||
.get('/api/v1/users') |
||||
.set('xc-auth', context.token) |
||||
.expect(200) |
||||
|
||||
const user = usersListResponse.body.list.find(u => u.email === args.email) |
||||
|
||||
expect(user).to.have.property('roles').to.be.equal(OrgUserRoles.VIEWER) |
||||
|
||||
|
||||
await request(context.app) |
||||
.patch('/api/v1/users/' + user.id) |
||||
.set('xc-auth', context.token) |
||||
.send({ roles: OrgUserRoles.CREATOR }) |
||||
.expect(200) |
||||
|
||||
|
||||
const signinResponse = await request(context.app) |
||||
.post('/api/v1/auth/user/signin') |
||||
// pass empty data in await request
|
||||
.send(args) |
||||
.expect(200) |
||||
|
||||
const response = await request(context.app) |
||||
.post('/api/v1/db/storage/upload') |
||||
.attach('files', FILE_PATH) |
||||
.set('xc-auth', signinResponse.body.token) |
||||
.expect(200) |
||||
|
||||
const attachments = response.body |
||||
expect(attachments).to.be.an('array') |
||||
expect(attachments[0].title).to.be.eq(path.basename(FILE_PATH)) |
||||
}) |
||||
|
||||
|
||||
it('Upload file - Org level viewer with editor role in a project', async () => { |
||||
|
||||
// signup a new user
|
||||
const args = { |
||||
email: 'dummyuser@example.com', |
||||
password: 'A1234abh2@dsad', |
||||
} |
||||
|
||||
await request(context.app) |
||||
.post('/api/v1/auth/user/signup') |
||||
.send(args) |
||||
.expect(200) |
||||
|
||||
const newProject = await createProject(context, { |
||||
title: 'NewTitle1', |
||||
}) |
||||
|
||||
// invite user to project with editor role
|
||||
await request(context.app) |
||||
.post(`/api/v1/db/meta/projects/${newProject.id}/users`) |
||||
.set('xc-auth', context.token) |
||||
.send({ |
||||
roles: ProjectRoles.EDITOR, |
||||
email: args.email, |
||||
project_id: newProject.id, |
||||
projectName: newProject.title, |
||||
}) |
||||
.expect(200) |
||||
|
||||
// signin to get user token
|
||||
const signinResponse = await request(context.app) |
||||
.post('/api/v1/auth/user/signin') |
||||
// pass empty data in await request
|
||||
.send(args) |
||||
.expect(200) |
||||
|
||||
const response = await request(context.app) |
||||
.post('/api/v1/db/storage/upload') |
||||
.attach('files', FILE_PATH) |
||||
.set('xc-auth', signinResponse.body.token) |
||||
.expect(200) |
||||
|
||||
const attachments = response.body |
||||
expect(attachments).to.be.an('array') |
||||
expect(attachments[0].title).to.be.eq(path.basename(FILE_PATH)) |
||||
}) |
||||
|
||||
} |
||||
|
||||
export default function() { |
||||
describe('Attachment', attachmentTests) |
||||
} |
@ -0,0 +1,25 @@
|
||||
import { expect } from '@playwright/test'; |
||||
import BasePage from '../../Base'; |
||||
import { FormPage } from '../Form'; |
||||
import { GalleryPage } from '../Gallery'; |
||||
import { GridPage } from '../Grid'; |
||||
import { KanbanPage } from '../Kanban'; |
||||
|
||||
export class BarcodeOverlay extends BasePage { |
||||
constructor(parent: GridPage | GalleryPage | KanbanPage | FormPage) { |
||||
super(parent.rootPage); |
||||
} |
||||
|
||||
get() { |
||||
return this.rootPage.locator(`.nc-barcode-large`); |
||||
} |
||||
|
||||
async verifyBarcodeSvgValue(expectedValue: string) { |
||||
const foundBarcodeSvg = await this.get().getByTestId('barcode').innerHTML(); |
||||
await expect(foundBarcodeSvg).toContain(expectedValue); |
||||
} |
||||
|
||||
async clickCloseButton() { |
||||
await this.get().locator('.ant-modal-close-x').click(); |
||||
} |
||||
} |
@ -0,0 +1,174 @@
|
||||
import { expect, test } from '@playwright/test'; |
||||
import { DashboardPage } from '../pages/Dashboard'; |
||||
import setup from '../setup'; |
||||
import { GridPage } from '../pages/Dashboard/Grid'; |
||||
|
||||
interface ExpectedBarcodeData { |
||||
referencedValue: string; |
||||
barcodeSvg: string; |
||||
} |
||||
|
||||
test.describe('Virtual Columns', () => { |
||||
let dashboard: DashboardPage; |
||||
let grid: GridPage; |
||||
let context: any; |
||||
|
||||
test.beforeEach(async ({ page }) => { |
||||
context = await setup({ page }); |
||||
dashboard = new DashboardPage(page, context.project); |
||||
grid = dashboard.grid; |
||||
}); |
||||
|
||||
test.describe('Barcode Column', () => { |
||||
const initiallyExpectedBarcodeCellValues: ExpectedBarcodeData[] = [ |
||||
{ |
||||
referencedValue: 'A Corua (La Corua)', |
||||
barcodeSvg: |
||||
'<rect x="0" y="0" width="486" height="142" style="fill:#ffffff;"></rect><g transform="translate(10, 10)" style="fill:#000000;"><rect x="0" y="0" width="4" height="100"></rect><rect x="6" y="0" width="2" height="100"></rect><rect x="12" y="0" width="2" height="100"></rect><rect x="22" y="0" width="2" height="100"></rect><rect x="26" y="0" width="2" height="100"></rect><rect x="34" y="0" width="4" height="100"></rect><rect x="44" y="0" width="4" height="100"></rect><rect x="50" y="0" width="4" height="100"></rect><rect x="58" y="0" width="4" height="100"></rect><rect x="66" y="0" width="2" height="100"></rect><rect x="74" y="0" width="2" height="100"></rect><rect x="82" y="0" width="4" height="100"></rect><rect x="88" y="0" width="2" height="100"></rect><rect x="96" y="0" width="8" height="100"></rect><rect x="106" y="0" width="2" height="100"></rect><rect x="110" y="0" width="2" height="100"></rect><rect x="116" y="0" width="2" height="100"></rect><rect x="122" y="0" width="8" height="100"></rect><rect x="132" y="0" width="2" height="100"></rect><rect x="138" y="0" width="8" height="100"></rect><rect x="150" y="0" width="2" height="100"></rect><rect x="154" y="0" width="2" height="100"></rect><rect x="160" y="0" width="2" height="100"></rect><rect x="164" y="0" width="4" height="100"></rect><rect x="176" y="0" width="4" height="100"></rect><rect x="182" y="0" width="4" height="100"></rect><rect x="190" y="0" width="4" height="100"></rect><rect x="198" y="0" width="2" height="100"></rect><rect x="206" y="0" width="4" height="100"></rect><rect x="214" y="0" width="2" height="100"></rect><rect x="220" y="0" width="2" height="100"></rect><rect x="228" y="0" width="4" height="100"></rect><rect x="234" y="0" width="6" height="100"></rect><rect x="242" y="0" width="2" height="100"></rect><rect x="248" y="0" width="2" height="100"></rect><rect x="252" y="0" width="4" height="100"></rect><rect x="264" y="0" width="4" height="100"></rect><rect x="270" y="0" width="4" height="100"></rect><rect x="278" y="0" width="4" height="100"></rect><rect x="286" y="0" width="2" height="100"></rect><rect x="294" y="0" width="2" height="100"></rect><rect x="302" y="0" width="4" height="100"></rect><rect x="308" y="0" width="2" height="100"></rect><rect x="316" y="0" width="8" height="100"></rect><rect x="326" y="0" width="2" height="100"></rect><rect x="330" y="0" width="2" height="100"></rect><rect x="336" y="0" width="2" height="100"></rect><rect x="342" y="0" width="8" height="100"></rect><rect x="352" y="0" width="2" height="100"></rect><rect x="358" y="0" width="8" height="100"></rect><rect x="370" y="0" width="2" height="100"></rect><rect x="374" y="0" width="2" height="100"></rect><rect x="380" y="0" width="2" height="100"></rect><rect x="384" y="0" width="4" height="100"></rect><rect x="396" y="0" width="4" height="100"></rect><rect x="404" y="0" width="2" height="100"></rect><rect x="410" y="0" width="2" height="100"></rect><rect x="418" y="0" width="6" height="100"></rect><rect x="428" y="0" width="4" height="100"></rect><rect x="436" y="0" width="2" height="100"></rect><rect x="440" y="0" width="4" height="100"></rect><rect x="450" y="0" width="6" height="100"></rect><rect x="458" y="0" width="2" height="100"></rect><rect x="462" y="0" width="4" height="100"></rect><text style="font: 20px monospace" text-anchor="middle" x="233" y="122">A Corua (La Corua)</text></g>', |
||||
}, |
||||
{ |
||||
referencedValue: 'Abha', |
||||
barcodeSvg: |
||||
'<rect x="0" y="0" width="178" height="142" style="fill:#ffffff;"></rect><g transform="translate(10, 10)" style="fill:#000000;"><rect x="0" y="0" width="4" height="100"></rect><rect x="6" y="0" width="2" height="100"></rect><rect x="12" y="0" width="2" height="100"></rect><rect x="22" y="0" width="2" height="100"></rect><rect x="26" y="0" width="2" height="100"></rect><rect x="34" y="0" width="4" height="100"></rect><rect x="44" y="0" width="2" height="100"></rect><rect x="50" y="0" width="2" height="100"></rect><rect x="60" y="0" width="4" height="100"></rect><rect x="66" y="0" width="2" height="100"></rect><rect x="72" y="0" width="4" height="100"></rect><rect x="84" y="0" width="2" height="100"></rect><rect x="88" y="0" width="2" height="100"></rect><rect x="94" y="0" width="2" height="100"></rect><rect x="98" y="0" width="4" height="100"></rect><rect x="110" y="0" width="6" height="100"></rect><rect x="118" y="0" width="2" height="100"></rect><rect x="124" y="0" width="4" height="100"></rect><rect x="132" y="0" width="4" height="100"></rect><rect x="142" y="0" width="6" height="100"></rect><rect x="150" y="0" width="2" height="100"></rect><rect x="154" y="0" width="4" height="100"></rect><text style="font: 20px monospace" text-anchor="middle" x="79" y="122">Abha</text></g>', |
||||
}, |
||||
]; |
||||
|
||||
const barcodeCellValuesForBerlin = { |
||||
referencedValue: 'Berlin', |
||||
barcodeSvg: |
||||
'<rect x="0" y="0" width="222" height="142" style="fill:#ffffff;"></rect><g transform="translate(10, 10)" style="fill:#000000;"><rect x="0" y="0" width="4" height="100"></rect><rect x="6" y="0" width="2" height="100"></rect><rect x="12" y="0" width="2" height="100"></rect><rect x="22" y="0" width="2" height="100"></rect><rect x="30" y="0" width="2" height="100"></rect><rect x="34" y="0" width="4" height="100"></rect><rect x="44" y="0" width="2" height="100"></rect><rect x="48" y="0" width="4" height="100"></rect><rect x="56" y="0" width="2" height="100"></rect><rect x="66" y="0" width="2" height="100"></rect><rect x="72" y="0" width="2" height="100"></rect><rect x="78" y="0" width="8" height="100"></rect><rect x="88" y="0" width="4" height="100"></rect><rect x="96" y="0" width="2" height="100"></rect><rect x="100" y="0" width="2" height="100"></rect><rect x="110" y="0" width="2" height="100"></rect><rect x="120" y="0" width="4" height="100"></rect><rect x="126" y="0" width="2" height="100"></rect><rect x="132" y="0" width="4" height="100"></rect><rect x="144" y="0" width="2" height="100"></rect><rect x="148" y="0" width="2" height="100"></rect><rect x="154" y="0" width="4" height="100"></rect><rect x="164" y="0" width="2" height="100"></rect><rect x="170" y="0" width="2" height="100"></rect><rect x="176" y="0" width="4" height="100"></rect><rect x="186" y="0" width="6" height="100"></rect><rect x="194" y="0" width="2" height="100"></rect><rect x="198" y="0" width="4" height="100"></rect><text style="font: 20px monospace" text-anchor="middle" x="101" y="122">Berlin</text></g>', |
||||
}; |
||||
|
||||
const barcodeCellValuesForIstanbul = { |
||||
referencedValue: 'Istanbul', |
||||
barcodeSvg: |
||||
'<rect x="0" y="0" width="266" height="142" style="fill:#ffffff;"></rect><g transform="translate(10, 10)" style="fill:#000000;"><rect x="0" y="0" width="4" height="100"></rect><rect x="6" y="0" width="2" height="100"></rect><rect x="12" y="0" width="2" height="100"></rect><rect x="22" y="0" width="4" height="100"></rect><rect x="32" y="0" width="2" height="100"></rect><rect x="40" y="0" width="2" height="100"></rect><rect x="44" y="0" width="2" height="100"></rect><rect x="48" y="0" width="8" height="100"></rect><rect x="60" y="0" width="2" height="100"></rect><rect x="66" y="0" width="2" height="100"></rect><rect x="72" y="0" width="8" height="100"></rect><rect x="82" y="0" width="2" height="100"></rect><rect x="88" y="0" width="2" height="100"></rect><rect x="94" y="0" width="2" height="100"></rect><rect x="98" y="0" width="4" height="100"></rect><rect x="110" y="0" width="4" height="100"></rect><rect x="122" y="0" width="2" height="100"></rect><rect x="126" y="0" width="2" height="100"></rect><rect x="132" y="0" width="2" height="100"></rect><rect x="138" y="0" width="2" height="100"></rect><rect x="148" y="0" width="4" height="100"></rect><rect x="154" y="0" width="2" height="100"></rect><rect x="160" y="0" width="8" height="100"></rect><rect x="172" y="0" width="2" height="100"></rect><rect x="176" y="0" width="4" height="100"></rect><rect x="184" y="0" width="2" height="100"></rect><rect x="188" y="0" width="2" height="100"></rect><rect x="198" y="0" width="4" height="100"></rect><rect x="204" y="0" width="4" height="100"></rect><rect x="214" y="0" width="4" height="100"></rect><rect x="220" y="0" width="4" height="100"></rect><rect x="230" y="0" width="6" height="100"></rect><rect x="238" y="0" width="2" height="100"></rect><rect x="242" y="0" width="4" height="100"></rect><text style="font: 20px monospace" text-anchor="middle" x="123" y="122">Istanbul</text></g>', |
||||
}; |
||||
|
||||
const barcodeCode39SvgForBerlin = |
||||
'<rect x="0" y="0" width="276" height="142" style="fill:#ffffff;"></rect><g transform="translate(10, 10)" style="fill:#000000;"><rect x="0" y="0" width="2" height="100"></rect><rect x="8" y="0" width="2" height="100"></rect><rect x="12" y="0" width="6" height="100"></rect><rect x="20" y="0" width="6" height="100"></rect><rect x="28" y="0" width="2" height="100"></rect><rect x="32" y="0" width="2" height="100"></rect><rect x="36" y="0" width="6" height="100"></rect><rect x="44" y="0" width="2" height="100"></rect><rect x="52" y="0" width="2" height="100"></rect><rect x="56" y="0" width="6" height="100"></rect><rect x="64" y="0" width="6" height="100"></rect><rect x="72" y="0" width="2" height="100"></rect><rect x="76" y="0" width="6" height="100"></rect><rect x="88" y="0" width="2" height="100"></rect><rect x="92" y="0" width="2" height="100"></rect><rect x="96" y="0" width="6" height="100"></rect><rect x="104" y="0" width="2" height="100"></rect><rect x="108" y="0" width="2" height="100"></rect><rect x="112" y="0" width="6" height="100"></rect><rect x="124" y="0" width="2" height="100"></rect><rect x="128" y="0" width="2" height="100"></rect><rect x="132" y="0" width="6" height="100"></rect><rect x="140" y="0" width="2" height="100"></rect><rect x="144" y="0" width="2" height="100"></rect><rect x="152" y="0" width="6" height="100"></rect><rect x="160" y="0" width="2" height="100"></rect><rect x="164" y="0" width="6" height="100"></rect><rect x="172" y="0" width="2" height="100"></rect><rect x="180" y="0" width="6" height="100"></rect><rect x="188" y="0" width="2" height="100"></rect><rect x="192" y="0" width="2" height="100"></rect><rect x="196" y="0" width="2" height="100"></rect><rect x="200" y="0" width="6" height="100"></rect><rect x="208" y="0" width="2" height="100"></rect><rect x="216" y="0" width="6" height="100"></rect><rect x="224" y="0" width="2" height="100"></rect><rect x="232" y="0" width="2" height="100"></rect><rect x="236" y="0" width="6" height="100"></rect><rect x="244" y="0" width="6" height="100"></rect><rect x="252" y="0" width="2" height="100"></rect><text style="font: 20px monospace" text-anchor="middle" x="128" y="122">BERLIN</text></g>'; |
||||
|
||||
const expectedBarcodeCellValuesAfterCityNameChange = [ |
||||
barcodeCellValuesForBerlin, |
||||
...initiallyExpectedBarcodeCellValues.slice(1), |
||||
]; |
||||
|
||||
async function barcodeColumnVerify(barcodeColumnTitle: string, expectedBarcodeCodeData: ExpectedBarcodeData[]) { |
||||
for (let i = 0; i < expectedBarcodeCodeData.length; i++) { |
||||
await grid.cell.verifyBarcodeCell({ |
||||
index: i, |
||||
columnHeader: barcodeColumnTitle, |
||||
expectedSvgValue: expectedBarcodeCodeData[i].barcodeSvg, |
||||
}); |
||||
} |
||||
} |
||||
test('creation, showing, updating value and change barcode column title and reference column', async () => { |
||||
// Add barcode code column referencing the City column
|
||||
// and compare the base64 encoded codes/src attributes for the first 3 rows.
|
||||
// Column data from City table (Sakila DB)
|
||||
/** |
||||
* City LastUpdate Address List Country |
||||
* A Corua (La Corua) 2006-02-15 04:45:25 939 Probolinggo Loop Spain |
||||
* Abha 2006-02-15 04:45:25 733 Mandaluyong Place Saudi Arabia |
||||
* Abu Dhabi 2006-02-15 04:45:25 535 Ahmadnagar Manor United Arab Emirates |
||||
*/ |
||||
// close 'Team & Auth' tab
|
||||
await dashboard.closeTab({ title: 'Team & Auth' }); |
||||
|
||||
await dashboard.treeView.openTable({ title: 'City' }); |
||||
|
||||
await grid.column.create({ |
||||
title: 'Barcode1', |
||||
type: 'Barcode', |
||||
barcodeValueColumnTitle: 'City', |
||||
}); |
||||
|
||||
await barcodeColumnVerify('Barcode1', initiallyExpectedBarcodeCellValues); |
||||
|
||||
await grid.cell.fillText({ columnHeader: 'City', index: 0, text: 'Berlin' }); |
||||
|
||||
await barcodeColumnVerify('Barcode1', expectedBarcodeCellValuesAfterCityNameChange); |
||||
|
||||
await grid.cell.get({ columnHeader: 'Barcode1', index: 0 }).click(); |
||||
const barcodeGridOverlay = grid.barcodeOverlay; |
||||
await barcodeGridOverlay.verifyBarcodeSvgValue(barcodeCellValuesForBerlin.barcodeSvg); |
||||
await barcodeGridOverlay.clickCloseButton(); |
||||
|
||||
// Change the barcode column title
|
||||
await grid.column.openEdit({ title: 'Barcode1' }); |
||||
await grid.column.fillTitle({ title: 'Barcode1 Renamed' }); |
||||
await grid.column.save({ isUpdated: true }); |
||||
await barcodeColumnVerify('Barcode1 Renamed', expectedBarcodeCellValuesAfterCityNameChange); |
||||
|
||||
// Change the referenced column title
|
||||
await grid.column.openEdit({ title: 'City' }); |
||||
await grid.column.fillTitle({ title: 'City Renamed' }); |
||||
await grid.column.save({ isUpdated: true }); |
||||
await barcodeColumnVerify('Barcode1 Renamed', expectedBarcodeCellValuesAfterCityNameChange); |
||||
|
||||
// Change the referenced column
|
||||
await grid.column.create({ title: 'New City Column' }); |
||||
await grid.cell.fillText({ columnHeader: 'New City Column', index: 0, text: 'Istanbul' }); |
||||
await grid.column.openEdit({ title: 'Barcode1 Renamed' }); |
||||
await grid.column.changeReferencedColumnForBarcode({ titleOfReferencedColumn: 'New City Column' }); |
||||
|
||||
await barcodeColumnVerify('Barcode1 Renamed', [barcodeCellValuesForIstanbul]); |
||||
|
||||
await dashboard.closeTab({ title: 'City' }); |
||||
}); |
||||
|
||||
test('deletion of the barcode column: a) directly and b) indirectly when the reference value column is deleted', async () => { |
||||
await dashboard.closeTab({ title: 'Team & Auth' }); |
||||
|
||||
await dashboard.treeView.openTable({ title: 'City' }); |
||||
|
||||
await grid.column.create({ title: 'column_name_a' }); |
||||
await grid.column.verify({ title: 'column_name_a' }); |
||||
await grid.column.create({ |
||||
title: 'Barcode2', |
||||
type: 'Barcode', |
||||
barcodeValueColumnTitle: 'column_name_a', |
||||
}); |
||||
await grid.column.verify({ title: 'Barcode2', isVisible: true }); |
||||
await grid.column.delete({ title: 'Barcode2' }); |
||||
await grid.column.verify({ title: 'Barcode2', isVisible: false }); |
||||
|
||||
await grid.column.create({ |
||||
title: 'Barcode2', |
||||
type: 'Barcode', |
||||
barcodeValueColumnTitle: 'column_name_a', |
||||
}); |
||||
await grid.column.verify({ title: 'Barcode2', isVisible: true }); |
||||
await grid.column.delete({ title: 'column_name_a' }); |
||||
await grid.column.verify({ title: 'Barcode2', isVisible: false }); |
||||
|
||||
await dashboard.closeTab({ title: 'City' }); |
||||
}); |
||||
|
||||
test('a) showing an error message for non-compatible barcode input and b) changing the format of the Barcode is reflected in the change of the actual rendered barcode', async () => { |
||||
await dashboard.closeTab({ title: 'Team & Auth' }); |
||||
|
||||
await dashboard.treeView.openTable({ title: 'City' }); |
||||
|
||||
await grid.column.create({ |
||||
title: 'Barcode1', |
||||
type: 'Barcode', |
||||
barcodeValueColumnTitle: 'City', |
||||
}); |
||||
|
||||
await grid.column.openEdit({ |
||||
title: 'Barcode1', |
||||
}); |
||||
await grid.column.changeBarcodeFormat({ barcodeFormatName: 'CODE39' }); |
||||
|
||||
await grid.cell.verifyBarcodeCellShowsInvalidInputMessage({ |
||||
index: 0, |
||||
columnHeader: 'Barcode1', |
||||
}); |
||||
|
||||
await grid.cell.fillText({ columnHeader: 'City', index: 0, text: 'Berlin' }); |
||||
|
||||
await barcodeColumnVerify('Barcode1', [{ referencedValue: 'Berlin', barcodeSvg: barcodeCode39SvgForBerlin }]); |
||||
}); |
||||
}); |
||||
}); |
Loading…
Reference in new issue