mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 23:53:48 +01:00
e0272ad4af
* Added new types for various file paths, all in the Paths folder. * TypeSafety and other helper functions related to these types * Added basic globbing support with * and ?. Currently only implemented for Script/Text, on nano and download terminal commands * Enforcing the new types throughout the codebase, plus whatever rewrites happened along the way * Server.textFiles is now a map * TextFile no longer uses a fn property, now it is filename * Added a shared ContentFile interface for shared functionality between TextFile and Script. * related to ContentFile change above, the player is now allowed to move a text file to a script file and vice versa. * File paths no longer conditionally start with slashes, and all directory names other than root have ending slashes. The player is still able to provide paths starting with / but this now indicates that the player is specifying an absolute path instead of one relative to root. * Singularized the MessageFilename and LiteratureName enums * Because they now only accept correct types, server.writeToXFile functions now always succeed (the only reasons they could fail before were invalid filepath). * Fix several issues with tab completion, which included pretty much a complete rewrite * Changed the autocomplete display options so there's less chance it clips outside the display area. * Turned CompletedProgramName into an enum. * Got rid of programsMetadata, and programs and DarkWebItems are now initialized immediately instead of relying on initializers called from the engine. * For any executable (program, cct, or script file) pathing can be used directly to execute without using the run command (previously the command had to start with ./ and it wasn't actually using pathing).
223 lines
8.3 KiB
TypeScript
223 lines
8.3 KiB
TypeScript
/* eslint-disable no-await-in-loop */
|
|
|
|
import { Player } from "../../../src/Player";
|
|
import { getTabCompletionPossibilities } from "../../../src/Terminal/getTabCompletionPossibilities";
|
|
import { Server } from "../../../src/Server/Server";
|
|
import { AddToAllServers, prestigeAllServers } from "../../../src/Server/AllServers";
|
|
import { LocationName } from "../../../src/Enums";
|
|
import { CodingContract } from "../../../src/CodingContracts";
|
|
import { asFilePath } from "../../../src/Paths/FilePath";
|
|
import { Directory, isAbsolutePath, isDirectoryPath, root } from "../../../src/Paths/Directory";
|
|
import { hasTextExtension } from "../../../src/Paths/TextFilePath";
|
|
import { hasScriptExtension } from "../../../src/Paths/ScriptFilePath";
|
|
import { LiteratureName } from "../../../src/Literature/data/LiteratureNames";
|
|
import { MessageFilename } from "../../../src/Message/MessageHelpers";
|
|
import { Terminal } from "../../../src/Terminal";
|
|
|
|
describe("getTabCompletionPossibilities", function () {
|
|
let closeServer: Server;
|
|
let farServer: Server;
|
|
|
|
beforeEach(() => {
|
|
prestigeAllServers();
|
|
Player.init();
|
|
|
|
closeServer = new Server({
|
|
ip: "8.8.8.8",
|
|
hostname: "near",
|
|
hackDifficulty: 1,
|
|
moneyAvailable: 70000,
|
|
numOpenPortsRequired: 0,
|
|
organizationName: LocationName.NewTokyoNoodleBar,
|
|
requiredHackingSkill: 1,
|
|
serverGrowth: 3000,
|
|
});
|
|
farServer = new Server({
|
|
ip: "4.4.4.4",
|
|
hostname: "far",
|
|
hackDifficulty: 1,
|
|
moneyAvailable: 70000,
|
|
numOpenPortsRequired: 0,
|
|
organizationName: LocationName.Sector12JoesGuns,
|
|
requiredHackingSkill: 1,
|
|
serverGrowth: 3000,
|
|
});
|
|
Player.getHomeComputer().serversOnNetwork.push(closeServer.hostname);
|
|
closeServer.serversOnNetwork.push(Player.getHomeComputer().hostname);
|
|
closeServer.serversOnNetwork.push(farServer.hostname);
|
|
farServer.serversOnNetwork.push(closeServer.hostname);
|
|
AddToAllServers(closeServer);
|
|
AddToAllServers(farServer);
|
|
});
|
|
|
|
it("completes the connect command, regardless of folder", async () => {
|
|
let options = await getTabCompletionPossibilities("connect ", root);
|
|
expect(options).toEqual(["near"]);
|
|
options = await getTabCompletionPossibilities("connect ", asDirectory("folder1/"));
|
|
expect(options).toEqual(["near"]);
|
|
Terminal.connectToServer("near");
|
|
options = await getTabCompletionPossibilities("connect ", root);
|
|
expect(options).toEqual(["home", "far"]);
|
|
options = await getTabCompletionPossibilities("connect h", asDirectory("folder1/"));
|
|
// Also test completion of a partially completed text
|
|
expect(options).toEqual(["home"]);
|
|
});
|
|
|
|
it("completes the buy command", async () => {
|
|
let options = await getTabCompletionPossibilities("buy ", root);
|
|
expect(options.sort()).toEqual(
|
|
[
|
|
"BruteSSH.exe",
|
|
"FTPCrack.exe",
|
|
"relaySMTP.exe",
|
|
"HTTPWorm.exe",
|
|
"SQLInject.exe",
|
|
"DeepscanV1.exe",
|
|
"DeepscanV2.exe",
|
|
"AutoLink.exe",
|
|
"ServerProfiler.exe",
|
|
"Formulas.exe",
|
|
].sort(),
|
|
);
|
|
// Also test that darkweb items will be completed if they have incorrect capitalization in progress
|
|
options = await getTabCompletionPossibilities("buy de", root);
|
|
expect(options.sort()).toEqual(["DeepscanV1.exe", "DeepscanV2.exe"].sort());
|
|
});
|
|
|
|
it("completes the scp command", async () => {
|
|
writeFiles();
|
|
let options = await getTabCompletionPossibilities("scp ", root);
|
|
expect(options.sort()).toEqual(
|
|
[
|
|
"note.txt",
|
|
"folder1/text.txt",
|
|
"folder1/text2.txt",
|
|
"hack.js",
|
|
"weaken.js",
|
|
"grow.js",
|
|
"old.script",
|
|
"folder1/test.js",
|
|
"anotherFolder/win.js",
|
|
LiteratureName.AGreenTomorrow,
|
|
].sort(),
|
|
);
|
|
// Test the second command argument (server name)
|
|
options = await getTabCompletionPossibilities("scp note.txt ", root);
|
|
expect(options).toEqual(["home", "near", "far"]);
|
|
});
|
|
|
|
it("completes the kill, tail, mem, and check commands", async () => {
|
|
writeFiles();
|
|
for (const command of ["kill", "tail", "mem", "check"]) {
|
|
let options = await getTabCompletionPossibilities(`${command} `, root);
|
|
expect(options.sort()).toEqual(scriptFilePaths);
|
|
// From a directory, show only the options in that directory
|
|
options = await getTabCompletionPossibilities(`${command} `, asDirectory("folder1/"));
|
|
expect(options.sort()).toEqual(["test.js"]);
|
|
// From a directory but with relative path .., show stuff in the resolved directory with the relative pathing included
|
|
options = await getTabCompletionPossibilities(`${command} ../`, asDirectory("folder1/"));
|
|
expect(options.sort()).toEqual(
|
|
[...scriptFilePaths.map((path) => "../" + path), "../folder1/", "../anotherFolder/"].sort(),
|
|
);
|
|
options = await getTabCompletionPossibilities(`${command} ../folder1/../anotherFolder/`, asDirectory("folder1/"));
|
|
expect(options.sort()).toEqual(["../folder1/../anotherFolder/win.js"]);
|
|
}
|
|
});
|
|
|
|
it("completes the nano commands", async () => {
|
|
writeFiles();
|
|
const contentFilePaths = [...scriptFilePaths, ...textFilePaths].sort();
|
|
const options = await getTabCompletionPossibilities("nano ", root);
|
|
expect(options.sort()).toEqual(contentFilePaths);
|
|
});
|
|
|
|
it("completes the rm command", async () => {
|
|
writeFiles();
|
|
const removableFilePaths = [
|
|
...scriptFilePaths,
|
|
...textFilePaths,
|
|
...contractFilePaths,
|
|
LiteratureName.AGreenTomorrow,
|
|
"NUKE.exe",
|
|
].sort();
|
|
const options = await getTabCompletionPossibilities("rm ", root);
|
|
expect(options.sort()).toEqual(removableFilePaths);
|
|
});
|
|
|
|
it("completes the run command", async () => {
|
|
writeFiles();
|
|
const runnableFilePaths = [...scriptFilePaths, ...contractFilePaths, "NUKE.exe"].sort();
|
|
let options = await getTabCompletionPossibilities("run ", root);
|
|
expect(options.sort()).toEqual(runnableFilePaths);
|
|
// Also check the same files
|
|
options = await getTabCompletionPossibilities("./", root);
|
|
expect(options.sort()).toEqual(
|
|
[...runnableFilePaths.map((path) => "./" + path), "./folder1/", "./anotherFolder/"].sort(),
|
|
);
|
|
});
|
|
|
|
it("completes the cat command", async () => {
|
|
writeFiles();
|
|
const cattableFilePaths = [
|
|
...scriptFilePaths,
|
|
...textFilePaths,
|
|
MessageFilename.TruthGazer,
|
|
LiteratureName.AGreenTomorrow,
|
|
].sort();
|
|
const options = await getTabCompletionPossibilities("cat ", root);
|
|
expect(options.sort()).toEqual(cattableFilePaths);
|
|
});
|
|
|
|
it("completes the download and mv commands", async () => {
|
|
writeFiles();
|
|
writeFiles();
|
|
const contentFilePaths = [...scriptFilePaths, ...textFilePaths].sort();
|
|
for (const command of ["download", "mv"]) {
|
|
const options = await getTabCompletionPossibilities(`${command} `, root);
|
|
expect(options.sort()).toEqual(contentFilePaths);
|
|
}
|
|
});
|
|
|
|
it("completes the ls and cd commands", async () => {
|
|
writeFiles();
|
|
for (const command of ["ls", "cd"]) {
|
|
const options = await getTabCompletionPossibilities(`${command} `, root);
|
|
expect(options.sort()).toEqual(["folder1/", "anotherFolder/"].sort());
|
|
}
|
|
});
|
|
});
|
|
|
|
function asDirectory(dir: string): Directory {
|
|
if (!isAbsolutePath(dir) || !isDirectoryPath(dir)) throw new Error(`Directory ${dir} failed typechecking`);
|
|
return dir;
|
|
}
|
|
const textFilePaths = ["note.txt", "folder1/text.txt", "folder1/text2.txt"];
|
|
const scriptFilePaths = [
|
|
"hack.js",
|
|
"weaken.js",
|
|
"grow.js",
|
|
"old.script",
|
|
"folder1/test.js",
|
|
"anotherFolder/win.js",
|
|
].sort();
|
|
const contractFilePaths = ["testContract.cct", "anothercontract.cct"];
|
|
function writeFiles() {
|
|
const home = Player.getHomeComputer();
|
|
for (const filename of textFilePaths) {
|
|
if (!hasTextExtension(filename)) {
|
|
throw new Error(`Text file ${filename} had the wrong extension.`);
|
|
}
|
|
home.writeToTextFile(asFilePath(filename), `File content for ${filename}`);
|
|
}
|
|
for (const filename of scriptFilePaths) {
|
|
if (!hasScriptExtension(filename)) {
|
|
throw new Error(`Script file ${filename} had the wrong extension.`);
|
|
}
|
|
home.writeToScriptFile(asFilePath(filename), `File content for ${filename}`);
|
|
}
|
|
for (const filename of contractFilePaths) {
|
|
home.contracts.push(new CodingContract(filename));
|
|
}
|
|
home.messages.push(LiteratureName.AGreenTomorrow, MessageFilename.TruthGazer);
|
|
}
|