Skip to content

Fixing issues in backlog #83

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions __tests__/api-writer/glua-api-writer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { apiDefinition as structApiDefinition, markup as structMarkup, json as s
import { markup as panelMarkup, apiDefinition as panelApiDefinition } from '../test-data/offline-sites/gmod-wiki/panel-slider';
import { markup as multiReturnFuncMarkup, apiDefinition as multiReturnFuncApiDefinition } from '../test-data/offline-sites/gmod-wiki/library-function-concommand-gettable';
import { markup as varargsFuncMarkup, apiDefinition as varargsFuncApiDefinition } from '../test-data/offline-sites/gmod-wiki/library-function-coroutine-resume';
import { Enum, LibraryFunction, WikiPage, WikiPageMarkupScraper } from '../../src/scrapers/wiki-page-markup-scraper';
import { Enum, LibraryFunction, PanelFunction, WikiPage, WikiPageMarkupScraper } from '../../src/scrapers/wiki-page-markup-scraper';
import { GluaApiWriter } from '../../src/api-writer/glua-api-writer';
import fetchMock from "jest-fetch-mock";

Expand Down Expand Up @@ -107,12 +107,12 @@ describe('GLua API Writer', () => {
url: 'na',
arguments: [
{
args: [ {
args: [{
name: 'intensity',
type: 'number',
description: 'The intensity of the explosion.',
default: '1000',
} ]
}]
}
],
returns: [
Expand Down Expand Up @@ -397,6 +397,28 @@ describe('GLua API Writer', () => {
expect(api).toEqual(`---![(Client)](https://github.com/user-attachments/assets/a5f6ba64-374d-42f0-b2f4-50e5c964e808) Returns where on the screen the specified position vector would appear.\n---\n---[View wiki](na)\n---@return ToScreenData # The created Structures/ToScreenData.\nfunction Vector.ToScreen() end\n\n`);
});

it('should support Panel type', () => {
const writer = new GluaApiWriter();
const api = writer.writePage(<PanelFunction>{
name: 'GetVBar',
address: 'DScrollPanel.GetVBar',
parent: 'DScrollPanel',
isPanelFunction: 'yes',
description: 'Returns the vertical scroll bar of the panel.',
realm: 'client',
type: 'panelfunc',
url: 'na',
returns: [
{
type: 'Panel{DVScrollBar}',
description: 'The DVScrollBar.',
},
],
});

expect(api).toEqual(`---![(Client)](https://github.com/user-attachments/assets/a5f6ba64-374d-42f0-b2f4-50e5c964e808) Returns the vertical scroll bar of the panel.\n---\n---[View wiki](na)\n---@return DVScrollBar # The DVScrollBar.\nfunction DScrollPanel:GetVBar() end\n\n`);
});

// number{ENUM_NAME} -> ENUM_NAME
it('should support enum type', () => {
const writer = new GluaApiWriter();
Expand Down
14 changes: 14 additions & 0 deletions __tests__/api-writer/markdownify.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { markdownifyDescription } from '../../src/scrapers/wiki-page-markup-scraper';
import * as cheerio from 'cheerio';

describe('markdownifyDescription', () => {
it('should handle spaces in links', async () => {
const textWithUrl = '<test>This is a <page>link with spaces</page>.</test>';
const $ = cheerio.load(textWithUrl, { xmlMode: true });
const result = markdownifyDescription($, $('test'));

expect(result).toBe(
'This is a [link with spaces](https://wiki.facepunch.com/gmod/link_with_spaces).'
);
});
});
35 changes: 35 additions & 0 deletions __tests__/api-writer/write-type.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { GluaApiWriter } from '../../src/api-writer/glua-api-writer';

describe('writeType', () => {
describe('callback functions', () => {
it('should write parameter names', async () => {
const result = GluaApiWriter.transformType('function', {
arguments: [
{ type: 'number', name: 'count' },
{ type: 'string', name: 'total' },
],
returns: [
{ type: 'number', name: 'sum' },
{ type: 'string', name: 'limit' },
],
});

expect(result).toEqual('fun(count: number, total: string):(sum: number, limit: string)');
});

it('should write ret1, ret2, etc when parameter names are missing', async () => {
const result = GluaApiWriter.transformType('function', {
arguments: [
{ type: 'number', name: 'count' },
{ type: 'string', name: '' },
],
returns: [
{ type: 'number', name: '' },
{ type: 'string', name: '' },
],
});

expect(result).toEqual('fun(count: number, arg1: string):(ret0: number, ret1: string)');
});
});
});
6 changes: 1 addition & 5 deletions __tests__/test-data/offline-sites/gmod-wiki/panel-slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ end
</example>`;

export const apiDefinition =
`---
---
---
--- A simple slider featuring an numeric display.
---
`--- A simple slider featuring an numeric display.
---@deprecated Panel:SetActionFunction and Panel:PostMessage. Use DNumSlider instead.
---@class Slider : Panel
local Slider = {}\n\n`;
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ This can also be set from map, see <page>Sandbox Specific Mapping</page></item>
</structure>`;

export const apiDefinition =
`---
--- Information about custom fields **all** entities can have.
`--- Information about custom fields **all** entities can have.
---
--- See also [Structures/ENT](https://wiki.facepunch.com/gmod/Structures/ENT)
---
---
---[View wiki](https://wiki.facepunch.com/gmod/Custom_Entity_Fields)
---@class Custom_Entity_Fields
local Custom_Entity_Fields = {}
Expand Down Expand Up @@ -136,94 +134,94 @@ Custom_Entity_Fields.m_RenderOrigin = nil
Custom_Entity_Fields.m_RenderAngles = nil\n\n`;

export const json = {
name: 'Custom_Entity_Fields',
address: 'Custom_Entity_Fields',
type: 'struct',
fields: [
{
name: 'GetEntityDriveMode',
type: 'function',
description: '`Serverside`, Sandbox and Sandbox derived only.\n\nCalled by the Drive property to override the default drive type, which is `drive_sandbox`.',
},
{
name: 'OnEntityCopyTableFinish',
type: 'function',
description: 'Documented at ENTITY:OnEntityCopyTableFinish.',
},
{
name: 'PostEntityCopy',
type: 'function',
description: 'Documented at ENTITY:PostEntityCopy.',
},
{
name: 'PostEntityPaste',
type: 'function',
description: 'Documented at ENTITY:PostEntityPaste.',
},
{
name: 'PreEntityCopy',
type: 'function',
description: 'Documented at ENTITY:PreEntityCopy.',
},
{
name: 'OnDuplicated',
type: 'function',
description: 'Documented at ENTITY:OnDuplicated.',
},
{
name: 'PhysgunDisabled',
type: 'boolean',
description: '`Shared`, Sandbox or Sandbox derived only.\n\nIf set to `true`, physgun will not be able to pick this entity up. This can also be set from map, see Sandbox Specific Mapping',
},
{
name: 'PhysgunPickup',
type: 'function',
description: '`Shared`, Sandbox or Sandbox derived only.\n\nCalled from GM:PhysgunPickup, overrides `PhysgunDisabled`',
},
{
name: 'm_tblToolsAllowed',
type: 'table',
description: '`Shared`, Sandbox or Sandbox derived only.\n\nControls which tools **and** properties can be used on this entity. Format is a list of strings where each string is the tool or property classname.\n\nThis can also be set from map, see Sandbox Specific Mapping',
},
{
name: 'GravGunPickupAllowed',
type: 'function',
description: 'Documented at ENTITY:GravGunPickupAllowed.',
},
{
name: 'GravGunPunt',
type: 'function',
description: 'Documented at ENTITY:GravGunPunt.',
},
{
name: 'CanProperty',
type: 'function',
description: 'Documented at ENTITY:CanProperty.',
},
{
name: 'CanTool',
type: 'function',
description: 'Documented at ENTITY:CanTool.',
},
{
name: 'CalcAbsolutePosition',
type: 'function',
description: 'Documented at ENTITY:CalcAbsolutePosition.',
},
{
name: 'RenderOverride',
type: 'function',
description: 'Documented at ENTITY:RenderOverride.',
},
{
name: 'm_RenderOrigin',
type: 'Vector',
description: '(Clientside) Do not use.',
},
{
name: 'm_RenderAngles',
type: 'Angle',
description: '(Clientside) Do not use.',
},
],
name: 'Custom_Entity_Fields',
address: 'Custom_Entity_Fields',
type: 'struct',
fields: [
{
name: 'GetEntityDriveMode',
type: 'function',
description: '`Serverside`, Sandbox and Sandbox derived only.\n\nCalled by the Drive property to override the default drive type, which is `drive_sandbox`.',
},
{
name: 'OnEntityCopyTableFinish',
type: 'function',
description: 'Documented at ENTITY:OnEntityCopyTableFinish.',
},
{
name: 'PostEntityCopy',
type: 'function',
description: 'Documented at ENTITY:PostEntityCopy.',
},
{
name: 'PostEntityPaste',
type: 'function',
description: 'Documented at ENTITY:PostEntityPaste.',
},
{
name: 'PreEntityCopy',
type: 'function',
description: 'Documented at ENTITY:PreEntityCopy.',
},
{
name: 'OnDuplicated',
type: 'function',
description: 'Documented at ENTITY:OnDuplicated.',
},
{
name: 'PhysgunDisabled',
type: 'boolean',
description: '`Shared`, Sandbox or Sandbox derived only.\n\nIf set to `true`, physgun will not be able to pick this entity up. This can also be set from map, see Sandbox Specific Mapping',
},
{
name: 'PhysgunPickup',
type: 'function',
description: '`Shared`, Sandbox or Sandbox derived only.\n\nCalled from GM:PhysgunPickup, overrides `PhysgunDisabled`',
},
{
name: 'm_tblToolsAllowed',
type: 'table',
description: '`Shared`, Sandbox or Sandbox derived only.\n\nControls which tools **and** properties can be used on this entity. Format is a list of strings where each string is the tool or property classname.\n\nThis can also be set from map, see Sandbox Specific Mapping',
},
{
name: 'GravGunPickupAllowed',
type: 'function',
description: 'Documented at ENTITY:GravGunPickupAllowed.',
},
{
name: 'GravGunPunt',
type: 'function',
description: 'Documented at ENTITY:GravGunPunt.',
},
{
name: 'CanProperty',
type: 'function',
description: 'Documented at ENTITY:CanProperty.',
},
{
name: 'CanTool',
type: 'function',
description: 'Documented at ENTITY:CanTool.',
},
{
name: 'CalcAbsolutePosition',
type: 'function',
description: 'Documented at ENTITY:CalcAbsolutePosition.',
},
{
name: 'RenderOverride',
type: 'function',
description: 'Documented at ENTITY:RenderOverride.',
},
{
name: 'm_RenderOrigin',
type: 'Vector',
description: '(Clientside) Do not use.',
},
{
name: 'm_RenderAngles',
type: 'Angle',
description: '(Clientside) Do not use.',
},
],
};
61 changes: 57 additions & 4 deletions __tests__/utils/string.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { removeNewlines, toLowerCamelCase, putCommentBeforeEachLine, safeFileName } from '../../src/utils/string';
import { removeNewlines, toLowerCamelCase, wrapInComment, safeFileName, unindentText } from '../../src/utils/string';

describe('toLowerCamelCase', () => {
it('should convert a string to lowerCamelCase', () => {
Expand Down Expand Up @@ -35,21 +35,74 @@ describe('putCommentBeforeEachLine', () => {
['hello\n\n\n\nworld', '--- hello\n---\n--- world'],
['hello\r\n\r\n\r\n\r\nworld', '--- hello\n---\n--- world'],
])('should put a comment before each line', (input, expected, skipFirstLine = false) => {
expect(putCommentBeforeEachLine(input, skipFirstLine)).toBe(expected);
expect(wrapInComment(input, skipFirstLine)).toBe(expected);
});
});

describe('wrapInComment', () => {
it.each([
[
`The following specifiers are exclusive to LuaJIT:

| Format | Description | Example of the output |
|:------:|:-----------:|:---------------------:|
| %p | Returns pointer to supplied structure (table/function) | \`0xf20a8968\` |
| %q | Formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter | \`"test\\1\\2test"\` |`,
`The following specifiers are exclusive to LuaJIT:
--[[

| Format | Description | Example of the output |
|:------:|:-----------:|:---------------------:|
| %p | Returns pointer to supplied structure (table/function) | \`0xf20a8968\` |
| %q | Formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter | \`"test\\1\\2test"\` |
--]]`,
true,
],
[
`The following specifiers are exclusive to LuaJIT:

| Format | Description | Example of the output |
| ------ | ----------- | --------------------- |
| %p | Returns pointer to supplied structure (table/function) | \`0xf20a8968\` |
| %q | Formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter | \`"test\\1\\2test"\` |`,
`--[[
The following specifiers are exclusive to LuaJIT:

| Format | Description | Example of the output |
| ------ | ----------- | --------------------- |
| %p | Returns pointer to supplied structure (table/function) | \`0xf20a8968\` |
| %q | Formats a string between double quotes, using escape sequences when necessary to ensure that it can safely be read back by the Lua interpreter | \`"test\\1\\2test"\` |
--]]`,
],
])('should put a comment before each line', (input, expected, skipFirstLine = false) => {
expect(wrapInComment(input, skipFirstLine)).toBe(expected);
});
});

describe('safeFileName', () => {
it.each([
['hello world','hello world'],
['hello world', 'hello world'],
['hello:World', 'hello_World'],
['hello:World', 'hello-World', '-'],
['hello:World', 'helloWorld', ''],
['hello:World', 'hello World', ' '],
])('should make a string "%s" safe ("%s") for use as a file name', (input: string, expected: string, replacement: string|undefined = undefined) => {
])('should make a string "%s" safe ("%s") for use as a file name', (input: string, expected: string, replacement: string | undefined = undefined) => {
if (replacement !== undefined)
expect(safeFileName(input, replacement)).toBe(expected);
else
expect(safeFileName(input)).toBe(expected);
});
});

describe('unindentText', () => {
it.each([
['hello\nworld', 'hello\nworld'],
[' hello\n world', 'hello\nworld'],
[' hello\n world', 'hello\nworld'],
['\thello\n\tworld', 'hello\nworld'],
['\thello\n\t world', 'hello\n world'],
['\t\thello\n\t\tworld', 'hello\nworld'],
])('should unindent text by the given amount', (input, expected) => {
expect(unindentText(input)).toBe(expected);
});
});
Loading