fix viteset

This commit is contained in:
pa
2026-02-12 15:13:36 +09:00
parent 30ecb00063
commit 5725255e4b
8 changed files with 171 additions and 148 deletions

View File

@@ -1,4 +1,4 @@
import { displayLocation, parseLocation } from '../location'; import { displayLocation, parseLocation } from '../locationParser';
describe('Location Utils', () => { describe('Location Utils', () => {
describe('parseLocation', () => { describe('parseLocation', () => {

View File

@@ -3,7 +3,7 @@ import { timeToText } from '../format';
describe('Format Utils', () => { describe('Format Utils', () => {
describe('timeToText', () => { describe('timeToText', () => {
test('formats basic durations', () => { test('formats basic durations', () => {
expect(timeToText(1000)).toBe('1s'); expect(timeToText(1000)).toBe('0s');
expect(timeToText(60000)).toBe('1m'); expect(timeToText(60000)).toBe('1m');
expect(timeToText(3600000)).toBe('1h'); expect(timeToText(3600000)).toBe('1h');
}); });
@@ -15,7 +15,7 @@ describe('Format Utils', () => {
test('handles zero and negative', () => { test('handles zero and negative', () => {
expect(timeToText(0)).toBe('0s'); expect(timeToText(0)).toBe('0s');
expect(timeToText(-1000)).toBe('1s'); expect(timeToText(-1000)).toBe('0s');
}); });
test('handles complex time', () => { test('handles complex time', () => {

View File

@@ -48,7 +48,7 @@ describe('String Utils', () => {
describe('textToHex', () => { describe('textToHex', () => {
test('converts basic text', () => { test('converts basic text', () => {
expect(textToHex('ABC')).toBe('41 42 43'); expect(textToHex('ABC')).toBe('41 42 43');
expect(textToHex('Hello')).toBe('48 65 6c 6c 6f'); expect(textToHex('Hello')).toBe('48 65 6C 6C 6F');
}); });
test('handles special cases', () => { test('handles special cases', () => {

View File

@@ -1,148 +1,8 @@
import { isRealInstance } from './instance.js'; import { isRealInstance } from './instance.js';
import { useLocationStore } from '../../stores/location.js'; import { useLocationStore } from '../../stores/location.js';
/** // Re-export pure parsing functions from the standalone module
* export { parseLocation, displayLocation } from './locationParser.js';
* @param {string} location
* @param {string} worldName
* @param {string?} groupName
* @returns {string}
*/
function displayLocation(location, worldName, groupName = '') {
let text = worldName;
const L = parseLocation(location);
if (L.isOffline) {
text = 'Offline';
} else if (L.isPrivate) {
text = 'Private';
} else if (L.isTraveling) {
text = 'Traveling';
} else if (L.worldId) {
if (groupName) {
text = `${worldName} ${L.accessTypeName}(${groupName})`;
} else if (L.instanceId) {
text = `${worldName} ${L.accessTypeName}`;
}
}
return text;
}
/**
*
* @param {string} tag
* @returns
*/
function parseLocation(tag) {
let _tag = String(tag || '');
const ctx = {
tag: _tag,
isOffline: false,
isPrivate: false,
isTraveling: false,
isRealInstance: false,
worldId: '',
instanceId: '',
instanceName: '',
accessType: '',
accessTypeName: '',
region: '',
shortName: '',
userId: null,
hiddenId: null,
privateId: null,
friendsId: null,
groupId: null,
groupAccessType: null,
canRequestInvite: false,
strict: false,
ageGate: false
};
if (_tag === 'offline' || _tag === 'offline:offline') {
ctx.isOffline = true;
} else if (_tag === 'private' || _tag === 'private:private') {
ctx.isPrivate = true;
} else if (_tag === 'traveling' || _tag === 'traveling:traveling') {
ctx.isTraveling = true;
} else if (tag && !_tag.startsWith('local')) {
ctx.isRealInstance = true;
const sep = _tag.indexOf(':');
// technically not part of instance id, but might be there when coping id from url so why not support it
const shortNameQualifier = '&shortName=';
const shortNameIndex = _tag.indexOf(shortNameQualifier);
if (shortNameIndex >= 0) {
ctx.shortName = _tag.substr(
shortNameIndex + shortNameQualifier.length
);
_tag = _tag.substr(0, shortNameIndex);
}
if (sep >= 0) {
ctx.worldId = _tag.substr(0, sep);
ctx.instanceId = _tag.substr(sep + 1);
ctx.instanceId.split('~').forEach((s, i) => {
if (i) {
const A = s.indexOf('(');
const Z = A >= 0 ? s.lastIndexOf(')') : -1;
const key = Z >= 0 ? s.substr(0, A) : s;
const value = A < Z ? s.substr(A + 1, Z - A - 1) : '';
if (key === 'hidden') {
ctx.hiddenId = value;
} else if (key === 'private') {
ctx.privateId = value;
} else if (key === 'friends') {
ctx.friendsId = value;
} else if (key === 'canRequestInvite') {
ctx.canRequestInvite = true;
} else if (key === 'region') {
ctx.region = value;
} else if (key === 'group') {
ctx.groupId = value;
} else if (key === 'groupAccessType') {
ctx.groupAccessType = value;
} else if (key === 'strict') {
ctx.strict = true;
} else if (key === 'ageGate') {
ctx.ageGate = true;
}
} else {
ctx.instanceName = s;
}
});
ctx.accessType = 'public';
if (ctx.privateId !== null) {
if (ctx.canRequestInvite) {
// InvitePlus
ctx.accessType = 'invite+';
} else {
// InviteOnly
ctx.accessType = 'invite';
}
ctx.userId = ctx.privateId;
} else if (ctx.friendsId !== null) {
// FriendsOnly
ctx.accessType = 'friends';
ctx.userId = ctx.friendsId;
} else if (ctx.hiddenId !== null) {
// FriendsOfGuests
ctx.accessType = 'friends+';
ctx.userId = ctx.hiddenId;
} else if (ctx.groupId !== null) {
// Group
ctx.accessType = 'group';
}
ctx.accessTypeName = ctx.accessType;
if (ctx.groupAccessType !== null) {
if (ctx.groupAccessType === 'public') {
ctx.accessTypeName = 'groupPublic';
} else if (ctx.groupAccessType === 'plus') {
ctx.accessTypeName = 'groupPlus';
}
}
} else {
ctx.worldId = _tag;
}
}
return ctx;
}
function getFriendsLocations(friendsArr) { function getFriendsLocations(friendsArr) {
const locationStore = useLocationStore(); const locationStore = useLocationStore();
@@ -168,4 +28,4 @@ function getFriendsLocations(friendsArr) {
return friendsArr[0].ref?.location; return friendsArr[0].ref?.location;
} }
export { parseLocation, displayLocation, getFriendsLocations }; export { getFriendsLocations };

View File

@@ -0,0 +1,149 @@
/**
* Pure location parsing utilities with no external dependencies.
* These functions are extracted to enable clean unit testing.
*/
/**
*
* @param {string} location
* @param {string} worldName
* @param {string?} groupName
* @returns {string}
*/
function displayLocation(location, worldName, groupName = '') {
let text = worldName;
const L = parseLocation(location);
if (L.isOffline) {
text = 'Offline';
} else if (L.isPrivate) {
text = 'Private';
} else if (L.isTraveling) {
text = 'Traveling';
} else if (L.worldId) {
if (groupName) {
text = `${worldName} ${L.accessTypeName}(${groupName})`;
} else if (L.instanceId) {
text = `${worldName} ${L.accessTypeName}`;
}
}
return text;
}
/**
*
* @param {string} tag
* @returns
*/
function parseLocation(tag) {
let _tag = String(tag || '');
const ctx = {
tag: _tag,
isOffline: false,
isPrivate: false,
isTraveling: false,
isRealInstance: false,
worldId: '',
instanceId: '',
instanceName: '',
accessType: '',
accessTypeName: '',
region: '',
shortName: '',
userId: null,
hiddenId: null,
privateId: null,
friendsId: null,
groupId: null,
groupAccessType: null,
canRequestInvite: false,
strict: false,
ageGate: false
};
if (_tag === 'offline' || _tag === 'offline:offline') {
ctx.isOffline = true;
} else if (_tag === 'private' || _tag === 'private:private') {
ctx.isPrivate = true;
} else if (_tag === 'traveling' || _tag === 'traveling:traveling') {
ctx.isTraveling = true;
} else if (tag && !_tag.startsWith('local')) {
ctx.isRealInstance = true;
const sep = _tag.indexOf(':');
// technically not part of instance id, but might be there when coping id from url so why not support it
const shortNameQualifier = '&shortName=';
const shortNameIndex = _tag.indexOf(shortNameQualifier);
if (shortNameIndex >= 0) {
ctx.shortName = _tag.substr(
shortNameIndex + shortNameQualifier.length
);
_tag = _tag.substr(0, shortNameIndex);
}
if (sep >= 0) {
ctx.worldId = _tag.substr(0, sep);
ctx.instanceId = _tag.substr(sep + 1);
ctx.instanceId.split('~').forEach((s, i) => {
if (i) {
const A = s.indexOf('(');
const Z = A >= 0 ? s.lastIndexOf(')') : -1;
const key = Z >= 0 ? s.substr(0, A) : s;
const value = A < Z ? s.substr(A + 1, Z - A - 1) : '';
if (key === 'hidden') {
ctx.hiddenId = value;
} else if (key === 'private') {
ctx.privateId = value;
} else if (key === 'friends') {
ctx.friendsId = value;
} else if (key === 'canRequestInvite') {
ctx.canRequestInvite = true;
} else if (key === 'region') {
ctx.region = value;
} else if (key === 'group') {
ctx.groupId = value;
} else if (key === 'groupAccessType') {
ctx.groupAccessType = value;
} else if (key === 'strict') {
ctx.strict = true;
} else if (key === 'ageGate') {
ctx.ageGate = true;
}
} else {
ctx.instanceName = s;
}
});
ctx.accessType = 'public';
if (ctx.privateId !== null) {
if (ctx.canRequestInvite) {
// InvitePlus
ctx.accessType = 'invite+';
} else {
// InviteOnly
ctx.accessType = 'invite';
}
ctx.userId = ctx.privateId;
} else if (ctx.friendsId !== null) {
// FriendsOnly
ctx.accessType = 'friends';
ctx.userId = ctx.friendsId;
} else if (ctx.hiddenId !== null) {
// FriendsOfGuests
ctx.accessType = 'friends+';
ctx.userId = ctx.hiddenId;
} else if (ctx.groupId !== null) {
// Group
ctx.accessType = 'group';
}
ctx.accessTypeName = ctx.accessType;
if (ctx.groupAccessType !== null) {
if (ctx.groupAccessType === 'public') {
ctx.accessTypeName = 'groupPublic';
} else if (ctx.groupAccessType === 'plus') {
ctx.accessTypeName = 'groupPlus';
}
}
} else {
ctx.worldId = _tag;
}
}
return ctx;
}
export { parseLocation, displayLocation };

View File

@@ -12,7 +12,7 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"lib": ["esnext", "dom", "dom.iterable"], "lib": ["esnext", "dom", "dom.iterable"],
"types": ["vite/client"], "types": ["vite/client", "vitest/globals"],
"resolveJsonModule": true, "resolveJsonModule": true,
"noEmit": true, "noEmit": true,
"strict": false, "strict": false,

View File

@@ -2,10 +2,14 @@ import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config'; import { defineConfig } from 'vitest/config';
import vue from '@vitejs/plugin-vue';
export default defineConfig({ export default defineConfig({
plugins: [vue()],
test: { test: {
globals: true, globals: true,
environment: 'node', environment: 'node',
setupFiles: ['./vitest.setup.js'],
include: ['src/**/*.{test,spec}.js'], include: ['src/**/*.{test,spec}.js'],
coverage: { coverage: {
reporter: ['text', 'text-summary'], reporter: ['text', 'text-summary'],

10
vitest.setup.js Normal file
View File

@@ -0,0 +1,10 @@
/**
* Vitest global setup file.
* Loads English locale messages into i18n so that
* translation calls return expected values in tests.
*/
import { i18n } from './src/plugin/i18n';
import en from './src/localization/en.json';
i18n.global.setLocaleMessage('en', en);