Compare commits

...

58 Commits

Author SHA1 Message Date
Olivier Gagnon
42704d8695 v0.52.9 2021-08-27 15:26:12 -04:00
Olivier Gagnon
e75197dee3 build 2021-08-27 14:19:36 -04:00
Olivier Gagnon
9e92df47a5 Added file diagnostic. 2021-08-27 14:17:25 -04:00
Olivier Gagnon
c110c22efb My corp infinity safeguard from 2 patch ago wasn't actually preventing it, just logging, now it returns to avoid it. 2021-08-27 11:18:06 -04:00
Olivier Gagnon
c9ab7908a7 another blocker against mku equal 0 and added tprintf 2021-08-27 11:05:36 -04:00
Olivier Gagnon
3ab306f9d7 fix the errors about node setTimeout instead of window 2021-08-27 01:11:11 -04:00
hydroflame
f08aa8924c Merge pull request #1102 from threehams/test-runner
Switch out test runner for jest
2021-08-27 00:53:45 -04:00
Olivier Gagnon
c4914fa54f build community prs 2021-08-27 00:45:11 -04:00
hydroflame
fa5e2f4964 Merge pull request #1079 from threehams/infil-instakill
Instakill player when automating infiltration
2021-08-26 21:42:08 -04:00
hydroflame
77eda1fd75 Merge pull request #1098 from brubsby/patch-1
add bladeburner_analysis_mult to getPlayer()
2021-08-26 21:42:01 -04:00
Olivier Gagnon
c987c91a11 add corp safeguard 2021-08-26 21:39:51 -04:00
David Edmondson
feaa74ed34 Only compile down imports during tests 2021-08-26 17:02:02 -07:00
David Edmondson
701fba7ec7 Drop cross-env 2021-08-26 16:45:39 -07:00
David Edmondson
51bd626e88 Remove unneeded stuff, .vscode on gitignore 2021-08-26 16:44:37 -07:00
David Edmondson
ab4863e7df Swap out mocha/chai for jest 2021-08-26 16:43:11 -07:00
David Edmondson
1a8bcf66cc Fix existing tests, update to jest 2021-08-26 16:43:03 -07:00
David Edmondson
7bfceb1690 Replace old-style import with type 2021-08-26 16:42:57 -07:00
David Edmondson
27e22814a9 Remove missing + unused variable 2021-08-26 16:42:47 -07:00
Olivier Gagnon
ceb4e304fd Hotfix corp mku getting set to zero and causing infinity 2021-08-26 15:22:06 -04:00
Olivier Gagnon
e2d74f9432 fix beautify 2021-08-25 16:14:47 -04:00
Olivier Gagnon
79345a49b4 Bladeburner automation status always displays the commands, even when disabled 2021-08-25 11:50:33 -04:00
Olivier Gagnon
7066a793a1 build fix 2021-08-24 21:40:50 -04:00
hydroflame
2a5cf62168 Merge pull request #1097 from Snarling/patch-2
Fix joining blade via ns
2021-08-24 21:39:29 -04:00
brubsby
6495be5705 add bladeburner_analysis_mult to getPlayer() 2021-08-24 20:02:39 -05:00
Snarling
0d6d05db49 Fix joining blade via ns
Pass Player as an argument in Bladeburner constructor call for ns.bladeburner.joinBladeburnerDivision()
2021-08-24 20:08:29 -04:00
Olivier Gagnon
5d59620dce click to copy every bladeburner action 2021-08-23 11:42:14 -04:00
Olivier Gagnon
60d95a90d0 Fix script not being saved on their individual computers. 2021-08-23 09:33:49 -04:00
Olivier Gagnon
51debc60da build omuretsu fix 2021-08-23 09:18:43 -04:00
Snarling
faf625b34d Update Root.tsx
Went back to tracking lastServer as a hostname, since server IPs are not static.
2021-08-23 04:04:52 -07:00
Snarling
1a8b194341 Update Root.tsx
Removed unnecessary conversions between server and ip
2021-08-23 04:04:52 -07:00
Snarling
386f8a11c5 Change lastServer to reference the server ip
Should fix issue with newly saved scripts failing to run
2021-08-23 04:04:52 -07:00
hydroflame
4278191b0e Merge pull request #1090 from danielyxie/dev
v0.52.8
2021-08-23 02:09:55 -04:00
Olivier Gagnon
6d2b8b4f6f v0.52.8 2021-08-23 02:09:49 -04:00
Olivier Gagnon
b148b2f0b5 logbox close on escape now 2021-08-23 01:15:20 -04:00
hydroflame
4a9bac99d2 Merge pull request #1083 from danielyxie/dev
Fix monaco jumping to end of file.
2021-08-22 23:57:16 -04:00
Olivier Gagnon
0b3c114cd0 Fix monaco jumping to end of file. 2021-08-22 23:57:00 -04:00
hydroflame
49cc75a575 Merge pull request #1082 from danielyxie/dev
trying to fix the jumping bug
2021-08-22 23:47:44 -04:00
Olivier Gagnon
e0d631f8b3 trying to fix the jumping bug 2021-08-22 23:46:30 -04:00
hydroflame
8289c9fc75 Merge pull request #1080 from danielyxie/dev
Fixed Script Editor not loading the same file after manually clicking it
2021-08-22 01:31:00 -04:00
Olivier Gagnon
d66e36b637 Fixed Script Editor not loading the same file after manually clicking it 2021-08-22 01:30:28 -04:00
David Edmondson
6cd7465b82 Instakill player when automating infiltration 2021-08-21 15:00:00 -07:00
hydroflame
c7125e2e46 Merge pull request #1077 from danielyxie/dev
Fix a few other bugs
2021-08-21 14:01:05 -04:00
hydroflame
480d47eece Merge pull request #1076 from danielyxie/dev
Fix log box dragging.
2021-08-21 02:39:17 -04:00
hydroflame
9ac75d5bf5 Merge pull request #1075 from danielyxie/dev
Fix Corp research popup box appearing behind one another.
2021-08-21 02:07:10 -04:00
hydroflame
1fb5105d0a Merge pull request #1074 from danielyxie/dev
hotfix broken editor shortcuts
2021-08-21 01:55:05 -04:00
hydroflame
7db3716256 Merge pull request #1072 from danielyxie/dev
hotfix the tutorial
2021-08-21 00:58:58 -04:00
hydroflame
aa3ad3164c Merge pull request #1068 from danielyxie/dev
v0.52.6
2021-08-21 00:32:04 -04:00
hydroflame
fea25249a8 Merge pull request #1062 from danielyxie/dev
v0.52.5
2021-08-19 16:38:26 -04:00
hydroflame
3826de72ef Merge pull request #1061 from danielyxie/dev
hotfix some blade netscript functions not working
2021-08-19 11:04:24 -04:00
hydroflame
5098ef6232 Merge pull request #1057 from danielyxie/dev
v0.52.4 - Bladeburner in React
2021-08-19 01:46:16 -04:00
hydroflame
27ee65f524 Merge pull request #1051 from danielyxie/dev
hotfix 0 territory being softlocked.
2021-08-17 17:47:58 -04:00
hydroflame
1d0f193c34 Merge pull request #1050 from danielyxie/dev
hotfix blocked in Gang
2021-08-17 17:14:11 -04:00
hydroflame
08908c87ea Merge pull request #1048 from danielyxie/dev
Hotfix weird bladeburner ui bug
2021-08-15 17:14:38 -04:00
hydroflame
3957a517db Merge pull request #1047 from danielyxie/dev
v0.52.3 - 2021-07-15 Gangs were OP (hydroflame)
2021-08-15 16:26:52 -04:00
hydroflame
21daab32c1 Merge pull request #1044 from danielyxie/dev
v0.52.2
2021-08-15 02:15:03 -04:00
hydroflame
5e2ed7a79e Merge pull request #1042 from danielyxie/dev
hotfix revert tutorial instructing the player to make a script on n00…
2021-08-11 01:05:46 -04:00
hydroflame
d9e60ea124 Merge pull request #1039 from danielyxie/dev
rebuild with the version inside the game correctly udpated
2021-08-10 21:10:00 -04:00
hydroflame
2750eb293a Merge pull request #1038 from danielyxie/dev
v0.52.1
2021-08-10 21:04:05 -04:00
65 changed files with 13407 additions and 3280 deletions

View File

@@ -1,3 +0,0 @@
{
"presets": ["@babel/preset-react"]
}

4
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.vscode
Changelog.txt
Netburner.txt
/doc/build
@@ -6,3 +7,6 @@ Netburner.txt
/test/*.map
/test/*.bundle.*
/test/*.css
# editor files
.vscode

9
babel.config.js Normal file
View File

@@ -0,0 +1,9 @@
const TEST = process.env.NODE_ENV === "test";
module.exports = {
"presets": [
"@babel/preset-react",
TEST && "@babel/preset-env",
TEST && "@babel/preset-typescript",
].filter(Boolean),
}

View File

@@ -112,6 +112,13 @@
background-color: #000;
}
.log-box-button:hover,
.log-box-button:focus {
color: var(--my-font-color);
text-decoration: none;
cursor: pointer;
}
.dialog-box-content {
z-index: 2;
background-color: var(--my-background-color);

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([863,0]),o()}({800:function(n,t,o){},802:function(n,t,o){},804:function(n,t,o){},806:function(n,t,o){},808:function(n,t,o){},810:function(n,t,o){},812:function(n,t,o){},814:function(n,t,o){},816:function(n,t,o){},818:function(n,t,o){},820:function(n,t,o){},822:function(n,t,o){},824:function(n,t,o){},826:function(n,t,o){},828:function(n,t,o){},830:function(n,t,o){},832:function(n,t,o){},834:function(n,t,o){},836:function(n,t,o){},838:function(n,t,o){},840:function(n,t,o){},842:function(n,t,o){},844:function(n,t,o){},846:function(n,t,o){},848:function(n,t,o){},850:function(n,t,o){},852:function(n,t,o){},854:function(n,t,o){},856:function(n,t,o){},858:function(n,t,o){},860:function(n,t,o){},863:function(n,t,o){"use strict";o.r(t);o(862),o(860),o(858),o(856),o(854),o(852),o(850),o(848),o(846),o(844),o(842),o(840),o(838),o(836),o(834),o(832),o(830),o(828),o(826),o(824),o(822),o(820),o(818),o(816),o(814),o(812),o(810),o(808),o(806),o(804),o(802),o(800)}});
!function(n){function t(t){for(var e,i,f=t[0],c=t[1],l=t[2],p=0,s=[];p<f.length;p++)i=f[p],u[i]&&s.push(u[i][0]),u[i]=0;for(e in c)Object.prototype.hasOwnProperty.call(c,e)&&(n[e]=c[e]);for(a&&a(t);s.length;)s.shift()();return r.push.apply(r,l||[]),o()}function o(){for(var n,t=0;t<r.length;t++){for(var o=r[t],e=!0,f=1;f<o.length;f++){var c=o[f];0!==u[c]&&(e=!1)}e&&(r.splice(t--,1),n=i(i.s=o[0]))}return n}var e={},u={1:0},r=[];function i(t){if(e[t])return e[t].exports;var o=e[t]={i:t,l:!1,exports:{}};return n[t].call(o.exports,o,o.exports,i),o.l=!0,o.exports}i.m=n,i.c=e,i.d=function(n,t,o){i.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:o})},i.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},i.t=function(n,t){if(1&t&&(n=i(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var e in n)i.d(o,e,function(t){return n[t]}.bind(null,e));return o},i.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return i.d(t,"a",t),t},i.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},i.p="";var f=window.webpackJsonp=window.webpackJsonp||[],c=f.push.bind(f);f.push=t,f=f.slice();for(var l=0;l<f.length;l++)t(f[l]);var a=c;r.push([866,0]),o()}({803:function(n,t,o){},805:function(n,t,o){},807:function(n,t,o){},809:function(n,t,o){},811:function(n,t,o){},813:function(n,t,o){},815:function(n,t,o){},817:function(n,t,o){},819:function(n,t,o){},821:function(n,t,o){},823:function(n,t,o){},825:function(n,t,o){},827:function(n,t,o){},829:function(n,t,o){},831:function(n,t,o){},833:function(n,t,o){},835:function(n,t,o){},837:function(n,t,o){},839:function(n,t,o){},841:function(n,t,o){},843:function(n,t,o){},845:function(n,t,o){},847:function(n,t,o){},849:function(n,t,o){},851:function(n,t,o){},853:function(n,t,o){},855:function(n,t,o){},857:function(n,t,o){},859:function(n,t,o){},861:function(n,t,o){},863:function(n,t,o){},866:function(n,t,o){"use strict";o.r(t);o(865),o(863),o(861),o(859),o(857),o(855),o(853),o(851),o(849),o(847),o(845),o(843),o(841),o(839),o(837),o(835),o(833),o(831),o(829),o(827),o(825),o(823),o(821),o(819),o(817),o(815),o(813),o(811),o(809),o(807),o(805),o(803)}});
//# sourceMappingURL=engineStyle.bundle.js.map

View File

@@ -2444,6 +2444,12 @@ input[type="checkbox"] {
border: 1px solid #fff;
background-color: #000; }
.log-box-button:hover,
.log-box-button:focus {
color: var(--my-font-color);
text-decoration: none;
cursor: pointer; }
.dialog-box-content {
z-index: 2;
background-color: var(--my-background-color);

30
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,57 @@
Changelog
=========
v0.52.9 - 2021-07-27 Less lag! (hydroflame & community)
-------------------------------------------
** Active Scripts page **
* Now less laggy, has pagination.
** File diagnostic **
* Added a popup found under options that shows the files you own and how
large they are. This help find bugs and leftover massive logs files.
** Corporation **
* Added safeguard against a very specific bug that causes NaN money. I'm
still not sure what the root cause is but it should prevent corp from
breaking.
** Netscript **
* tprintf is a new function that doesn't print the filename.
** Misc. **
* Infiltration kills you if you try to automate it. (@threehams)
* Fix beautify button not working
* Added bladeburner_analysis_mult to getPlayer() (@brubsby)
* Fixed joining bladeburner via netscript functions. (@omuretsu)
* All bladeburner actions are click-to-copy
* nerf noodle bar
v0.52.8 - 2021-07-23 Fixing the previous patch tbh ROUND 2 (hydroflame)
-------------------------------------------
** Script editor **
* Correctly reloads old script when clicking "Script Editor"
* No longer jumps to the end of the text for no reason.
** Hash upgrades **
* Fixed an issue where the default option would say ecorp but was really
foodnstuff
** Misc. **
* The "Delete all active script" button under the options has a clearer
description.
* Removed some debug console.log
* nerf noodle bar
v0.52.7 - 2021-07-21 Fixing the previous patch tbh (hydroflame)
-------------------------------------------

View File

@@ -66,7 +66,7 @@ documentation_title = '{0} Documentation'.format(project)
# The short X.Y version.
version = '0.52'
# The full version, including alpha/beta/rc tags.
release = '0.52.7'
release = '0.52.9'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -0,0 +1,17 @@
tprint() Netscript Function
===========================
.. js:function:: tprintf(format, args...)
:RAM cost: 0 GB
:param format: Format of the string to be printed.
:param args: Values to be formatted
Prints a raw formatted string to the terminal.
Example:
.. code-block:: javascript
tprintf("Hello world!"); // Prints "Hello world!" to the terminal.
tprintf("Hello %s", "world!"); // Prints "Hello world!" to the terminal.

View File

@@ -19,6 +19,7 @@ This includes information such as function signatures, what they do, and their r
sleep() <basicfunctions/sleep>
print() <basicfunctions/print>
tprint() <basicfunctions/tprint>
tprintf() <basicfunctions/tprint>
clearLog() <basicfunctions/clearLog>
disableLog() <basicfunctions/disableLog>
enableLog() <basicfunctions/enableLog>

View File

@@ -581,10 +581,12 @@
Copy Save data to Clipboard
</button>
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
Delete all active scripts
Force kill all active scripts
<span class="tooltiptextleft">
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
using this, save the game and then reload the page.
using this, save the game and then reload the page. This is different then normal kill in that normal kill
will tell the script to shut down while force kill just removes the references to it (and it should crash on it's own).
This will not remove the files on your computer. Just forcefully kill all running instance of all scripts.
</span>
</button>
<button id="debug-soft-reset" class="a-link-button tooltip">
@@ -593,6 +595,14 @@
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
</span>
</button>
<button id="debug-files" class="a-link-button tooltip">
Diagnose files
<span class="tooltiptextleft">
If your save file is extremely big you can use this button
to view a map of all the files on every server. Be careful
there might be spoilers.
</span>
</button>
</div>
</div>
</div>

9
jest.config.js Normal file
View File

@@ -0,0 +1,9 @@
module.exports = {
setupFiles: ["./jest.setup.js"],
moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
transform: {
"^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
},
// testMatch: ["**/?(*.)+(test).[jt]s?(x)"],
testEnvironment: "jsdom",
};

2
jest.setup.js Normal file
View File

@@ -0,0 +1,2 @@
import "regenerator-runtime/runtime";
global.$ = require("jquery");

14676
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -50,17 +50,18 @@
"description": "A cyberpunk-themed incremental game",
"devDependencies": {
"@babel/core": "^7.3.4",
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.0.0",
"@types/chai": "^4.1.7",
"@babel/preset-typescript": "^7.15.0",
"@types/jest": "^27.0.1",
"@types/lodash": "^4.14.168",
"@types/mocha": "^5.2.7",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"babel-jest": "^27.0.6",
"babel-loader": "^8.0.5",
"beautify-lint": "^1.0.3",
"benchmark": "^2.1.1",
"bundle-loader": "~0.5.0",
"chai": "^4.2.0",
"css-loader": "^0.28.11",
"es6-promise-polyfill": "^1.1.1",
"eslint": "^7.24.0",
@@ -69,6 +70,7 @@
"html-webpack-plugin": "^3.2.0",
"i18n-webpack-plugin": "^1.0.0",
"istanbul": "^0.4.5",
"jest": "^27.0.6",
"js-beautify": "^1.5.10",
"jsdom": "^15.0.0",
"jsdom-global": "^3.0.2",
@@ -78,10 +80,9 @@
"lodash": "^4.17.21",
"mini-css-extract-plugin": "^0.4.1",
"mkdirp": "^0.5.1",
"mocha": "^6.1.4",
"mochapack": "^1.1.1",
"null-loader": "^1.0.0",
"raw-loader": "~0.5.0",
"regenerator-runtime": "^0.13.9",
"sass-loader": "^7.0.3",
"script-loader": "~0.7.0",
"should": "^11.1.1",
@@ -124,10 +125,10 @@
"lint:jsts": "eslint --fix '*.{js,jsx,ts,tsx}' './src/**/*.{js,jsx,ts,tsx}' './test/**/*.{js,jsx,ts,tsx}' './utils/**/*.{js,jsx,ts,tsx}'",
"lint:style": "stylelint --fix ./css/*",
"preinstall": "node ./scripts/engines-check.js",
"test": "mochapack --webpack-config webpack.config-test.js -r jsdom-global/register ./test/index.js",
"test:container": "mochapack --webpack-config webpack.config-test.js --slow 2000 --timeout 10000 -r jsdom-global/register ./test/index.js",
"test": "jest",
"test:watch": "jest --watch",
"watch": "webpack --watch --mode production",
"watch:dev": "webpack --watch --mode development"
},
"version": "0.52.7"
"version": "0.52.9"
}

View File

@@ -606,13 +606,11 @@ export class Bladeburner implements IBladeburner {
const flag = args[1];
if (flag.toLowerCase() === "status") {
this.postToConsole("Automation: " + (this.automateEnabled ? "enabled" : "disabled"));
if (this.automateEnabled) {
this.postToConsole("When your stamina drops to " + formatNumber(this.automateThreshLow, 0) +
", you will automatically switch to " + this.automateActionLow.name +
". When your stamina recovers to " +
formatNumber(this.automateThreshHigh, 0) + ", you will automatically " +
"switch to " + this.automateActionHigh.name + ".");
}
this.postToConsole("When your stamina drops to " + formatNumber(this.automateThreshLow, 0) +
", you will automatically switch to " + this.automateActionLow.name +
". When your stamina recovers to " +
formatNumber(this.automateThreshHigh, 0) + ", you will automatically " +
"switch to " + this.automateActionHigh.name + ".");
} else if (flag.toLowerCase().includes("en")) {
if (!(this.automateActionLow instanceof ActionIdentifier) ||

View File

@@ -11,6 +11,7 @@ import { TeamSizePopup } from "./TeamSizePopup";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { SuccessChance } from "./SuccessChance";
import { CopyableText } from "../../ui/React/CopyableText";
interface IProps {
bladeburner: IBladeburner;
@@ -51,8 +52,8 @@ export function BlackOpElem(props: IProps): React.ReactElement {
return (<>
<h2 style={{display: 'inline-block'}}>
{isActive ?
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
<>{props.action.name}</>
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
<CopyableText value={props.action.name} />
}
</h2>
{isActive ?

View File

@@ -2,6 +2,7 @@ import * as React from "react";
import { BlackOpList } from "./BlackOpList";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CopyableText } from "../../ui/React/CopyableText";
interface IProps {
bladeburner: IBladeburner;

View File

@@ -26,18 +26,8 @@ export function Console(props: IProps): React.ReactElement {
// TODO: Figure out how to actually make the scrolling work correctly.
function scrollToBottom(): void {
function isMaxed(): boolean {
if(!lastRef.current) return false;
const oldTop = lastRef.current.scrollTop;
lastRef.current.scrollTop = lastRef.current.scrollHeight;
const maxed = oldTop === lastRef.current.scrollTop;
lastRef.current.scrollTop = oldTop;
return maxed;
}
if(lastRef.current) {
if(isMaxed())
lastRef.current.scrollTop = lastRef.current.scrollHeight;
}
if(!lastRef.current) return;
lastRef.current.scrollTop = lastRef.current.scrollHeight;
}
function rerender(): void {

View File

@@ -10,6 +10,7 @@ import { BladeburnerConstants } from "../data/Constants";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { SuccessChance } from "./SuccessChance";
import { CopyableText } from "../../ui/React/CopyableText";
interface IProps {
bladeburner: IBladeburner;
@@ -54,8 +55,8 @@ export function ContractElem(props: IProps): React.ReactElement {
return (<>
<h2 style={{display: 'inline-block'}}>
{isActive ?
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
<>{props.action.name}</>
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
<CopyableText value={props.action.name} />
}
</h2>
{isActive ?

View File

@@ -7,6 +7,7 @@ import {
} from "../../../utils/StringHelperFunctions";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { CopyableText } from "../../ui/React/CopyableText";
interface IProps {
bladeburner: IBladeburner;
@@ -43,8 +44,8 @@ export function GeneralActionElem(props: IProps): React.ReactElement {
return (<>
<h2 style={{display: 'inline-block'}}>
{isActive ?
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
<>{props.action.name}</>
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
<CopyableText value={props.action.name} />
}
</h2>
{isActive ?

View File

@@ -12,6 +12,7 @@ import { TeamSizePopup } from "./TeamSizePopup";
import { IBladeburner } from "../IBladeburner";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { SuccessChance } from "./SuccessChance";
import { CopyableText } from "../../ui/React/CopyableText";
interface IProps {
bladeburner: IBladeburner;
@@ -64,8 +65,8 @@ export function OperationElem(props: IProps): React.ReactElement {
return (<>
<h2 style={{display: 'inline-block'}}>
{isActive ?
<>{props.action.name} (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
<>{props.action.name}</>
<><CopyableText value={props.action.name} /> (IN PROGRESS - {formatNumber(computedActionTimeCurrent, 0)} / {formatNumber(props.bladeburner.actionTimeToComplete, 0)})</> :
<CopyableText value={props.action.name} />
}
</h2>
{isActive ?

View File

@@ -147,7 +147,7 @@ export class Roulette extends Game<IProps, IState> {
componentDidMount(): void {
this.interval = setInterval(this.step, 50);
this.interval = window.setInterval(this.step, 50);
}
step(): void {

View File

@@ -87,7 +87,7 @@ export class SlotMachine extends Game<IProps, IState> {
}
componentDidMount(): void {
this.interval = setInterval(this.step, 50);
this.interval = window.setInterval(this.step, 50);
}
step(): void {

View File

@@ -6,7 +6,7 @@
import { IMap } from "./types";
export const CONSTANTS: IMap<any> = {
Version: "0.52.7",
Version: "0.52.9",
// Speed (in ms) at which the main loop is updated
_idleSpeed: 200,
@@ -228,32 +228,35 @@ export const CONSTANTS: IMap<any> = {
TotalNumBitNodes: 24,
LatestUpdate: `
v0.52.7 - 2021-07-21 Fixing the previous patch tbh (hydroflame)
v0.52.9 - 2021-07-27 Less lag! (hydroflame & community)
-------------------------------------------
** Active Scripts page **
* Now less laggy, has pagination.
** File diagnostic **
* Added a popup found under options that shows the files you own and how
large they are. This help find bugs and leftover massive logs files.
** Corporation **
* Added safeguard against a very specific bug that causes NaN money. I'm
still not sure what the root cause is but it should prevent corp from
breaking.
** Netscript **
* API BREAKING CHANGE: getActionEstimatedSuccessChance now returns a pair of
value to reflect the UI changes. I'm very sorry.
** Bladeburner **
* General actions now display time required.
* Recruitment now displays success chance.
* All other success chance now display a range instead of a single value
The real value is guaranteed to be within that range.
* tprintf is a new function that doesn't print the filename.
** Misc. **
* Fix tutorial not working after Monaco upate
* Fix logbox logs not taking up the whole logbox
* Fix script editor shortcut (ctrl+b)
* Fix Corporation popup appearing in the wrong order, hiding one of them
* Fix error when loading Corp
* Fix logbox dragging (smoother now)
* Fix logbox name collision
* Fix logbox allowing to open the same box multiple times
* Fix netscript write.
* Infiltration kills you if you try to automate it. (@threehams)
* Fix beautify button not working
* Added bladeburner_analysis_mult to getPlayer() (@brusby)
* Fixed joining bladeburner via netscript functions. (@omuretsu)
* All bladeburner actions are click-to-copy
* nerf noodle bar
`,

View File

@@ -958,7 +958,6 @@ Industry.prototype.processProducts = function(marketCycles=1, corporation) {
const mgmtProd = office.employeeProd[EmployeePositions.Management];
const opProd = office.employeeProd[EmployeePositions.Operations];
const total = engrProd + mgmtProd + opProd;
if (total <= 0) { break; }
// Management is a multiplier for the production from Engineers
@@ -1113,8 +1112,13 @@ Industry.prototype.processProduct = function(marketCycles=1, product, corporatio
} else if (product.marketTa1) {
sCost = product.pCost + markupLimit;
} else if (isString(product.sCost)) {
if(product.mku === 0) {
console.error(`mku is zero, reverting to 1 to avoid Infinity`);
product.mku = 1;
}
sCost = product.sCost.replace(/MP/g, product.pCost + product.rat / product.mku);
sCost = eval(sCost);
} else {
sCost = product.sCost;
}
@@ -1440,11 +1444,11 @@ Industry.prototype.createResearchBox = function() {
}
Industry.prototype.toJSON = function() {
return Generic_toJSON("Industry", this);
return Generic_toJSON("Industry", this);
}
Industry.fromJSON = function(value) {
return Generic_fromJSON(Industry, value.data);
return Generic_fromJSON(Industry, value.data);
}
Reviver.constructors.Industry = Industry;
@@ -1601,11 +1605,11 @@ Employee.prototype.createUI = function(panel, corporation, industry) {
}
Employee.prototype.toJSON = function() {
return Generic_toJSON("Employee", this);
return Generic_toJSON("Employee", this);
}
Employee.fromJSON = function(value) {
return Generic_fromJSON(Employee, value.data);
return Generic_fromJSON(Employee, value.data);
}
Reviver.constructors.Employee = Employee;
@@ -1900,11 +1904,11 @@ OfficeSpace.prototype.unassignEmployeeFromJob = function(job) {
}
OfficeSpace.prototype.toJSON = function() {
return Generic_toJSON("OfficeSpace", this);
return Generic_toJSON("OfficeSpace", this);
}
OfficeSpace.fromJSON = function(value) {
return Generic_fromJSON(OfficeSpace, value.data);
return Generic_fromJSON(OfficeSpace, value.data);
}
Reviver.constructors.OfficeSpace = OfficeSpace;
@@ -1942,6 +1946,14 @@ function Corporation(params={}) {
this.state = new CorporationState();
}
Corporation.prototype.addFunds = function(amt) {
if(!isFinite(amt)) {
console.error('Trying to add invalid amount of funds. Report to a developper.');
return;
}
this.funds = this.funds.plus(amt);
}
Corporation.prototype.getState = function() {
return this.state.getState();
}
@@ -1981,7 +1993,7 @@ Corporation.prototype.process = function() {
});
var profit = this.revenue.minus(this.expenses);
const cycleProfit = profit.times(marketCycles * SecsPerMarketCycle);
if (isNaN(this.funds)) {
if (isNaN(this.funds) || this.funds === Infinity || this.funds === -Infinity) {
dialogBoxCreate("There was an error calculating your Corporations funds and they got reset to 0. " +
"This is a bug. Please report to game developer.<br><br>" +
"(Your funds have been set to $150b for the inconvenience)");
@@ -2000,10 +2012,10 @@ Corporation.prototype.process = function() {
const profit = this.numShares * dividendsPerShare * (1 - (this.dividendTaxPercentage / 100));
Player.gainMoney(profit);
Player.recordMoneySource(profit, "corporation");
this.funds = this.funds.plus(retainedEarnings);
this.addFunds(retainedEarnings);
}
} else {
this.funds = this.funds.plus(cycleProfit);
this.addFunds(cycleProfit);
}
this.updateSharePrice();
@@ -2070,7 +2082,7 @@ Corporation.prototype.getInvestment = function() {
noBtn.innerHML = "Reject";
yesBtn.addEventListener("click", () => {
++this.fundingRound;
this.funds = this.funds.plus(funding);
this.addFunds(funding);
this.numShares -= investShares;
this.rerender();
return yesNoBoxClose();
@@ -2124,7 +2136,7 @@ Corporation.prototype.goPublic = function() {
this.sharePrice = initialSharePrice;
this.issuedShares = numShares;
this.numShares -= numShares;
this.funds = this.funds.plus(numShares * initialSharePrice);
this.addFunds(numShares * initialSharePrice);
this.rerender();
removeElementById(goPublicPopupId);
dialogBoxCreate(`You took your ${this.name} public and earned ` +
@@ -2377,11 +2389,11 @@ Corporation.prototype.clearUI = function() {
}
Corporation.prototype.toJSON = function() {
return Generic_toJSON("Corporation", this);
return Generic_toJSON("Corporation", this);
}
Corporation.fromJSON = function(value) {
return Generic_fromJSON(Corporation, value.data);
return Generic_fromJSON(Corporation, value.data);
}
Reviver.constructors.Corporation = Corporation;

View File

@@ -187,6 +187,11 @@ export class Product {
this.calculateRating(industry);
const advMult = 1 + (Math.pow(this.advCost, 0.1) / 100);
this.mku = 100 / (advMult * Math.pow((this.qlt + 0.001), 0.65) * (busRatio + mgmtRatio));
// I actually don't understand well enough to know if this is right.
// I'm adding this to prevent a crash.
if(this.mku === 0) this.mku = 1;
this.dmd = industry.awareness === 0 ? 20 : Math.min(100, advMult * (100 * (industry.popularity / industry.awareness)));
this.cmp = getRandomInt(0, 70);

View File

@@ -502,7 +502,7 @@ class DevMenuComponent extends Component {
modifyBladeburnerRank(modify) {
return function(rank) {
if (Player.bladeburner) {
Player.bladeburner.changeRank(rank*modify);
Player.bladeburner.changeRank(Player, rank*modify);
}
}
}
@@ -514,7 +514,7 @@ class DevMenuComponent extends Component {
addTonsBladeburnerRank() {
if (Player.bladeburner) {
Player.bladeburner.changeRank(tonsP);
Player.bladeburner.changeRank(Player, tonsP);
}
}

View File

@@ -0,0 +1,68 @@
import React from "react";
import { AllServers } from "../Server/AllServers";
import { Script } from "../Script/Script";
import { TextFile } from "../TextFile";
import { Accordion } from "../ui/React/Accordion";
import { numeralWrapper } from "../ui/numeralFormat";
interface IServerProps {
ip: string;
}
export function ServerAccordion(props: IServerProps): React.ReactElement {
const server = AllServers[props.ip];
let totalSize = 0;
for(const f of server.scripts) {
totalSize += f.code.length;
}
for(const f of server.textFiles) {
totalSize += f.text.length;
}
if(totalSize === 0) {
return <></>
}
interface File {
name: string;
size: number;
}
const files: File[] = [];
for(const f of server.scripts) {
files.push({name: f.filename, size: f.code.length});
}
for(const f of server.textFiles) {
files.push({name: f.fn, size: f.text.length});
}
files.sort((a: File, b: File): number => b.size-a.size);
return <Accordion
headerContent={<>{server.hostname} ({numeralWrapper.formatBigNumber(totalSize)}b)</>}
panelContent={<ul>
{files.map((file: File) => <li key={file.name}>{file.name}: {numeralWrapper.formatBigNumber(file.size)}b</li>) }
</ul>}
/>
}
interface IProps {}
export function FileDiagnosticPopup(props: IProps): React.ReactElement {
const ips: string[] = [];
for(const ip of Object.keys(AllServers)) {
ips.push(ip);
}
return (<>
<p>
Welcome to the file diagnostic! If your save file is really big it's
likely because you have too many text/scripts. This tool can help you
narrow down where they are.
</p>
{ips.map((ip: string) => <ServerAccordion key={ip} ip={ip} />)}
</>);
}

View File

@@ -23,7 +23,7 @@ class HashUpgrade extends React.Component {
super(props);
this.state = {
selectedServer: "foodnstuff",
selectedServer: "ecorp",
}
this.changeTargetServer = this.changeTargetServer.bind(this);

View File

@@ -46,7 +46,7 @@ export function BackwardGame(props: IMinigameProps): React.ReactElement {
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1 className={"noselect"}>Type it backward</h1>
<KeyHandler onKeyDown={press} />
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
<Grid item xs={6}>
<p style={{transform: 'scaleX(-1)'}}>{answer}</p>

View File

@@ -78,7 +78,7 @@ export function BracketGame(props: IMinigameProps): React.ReactElement {
<Grid item xs={12}>
<h1 className={"noselect"}>Close the brackets</h1>
<p style={{fontSize: '5em'}}>{`${left}${right}`}<BlinkingCursor /></p>
<KeyHandler onKeyDown={press} />
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>)
}

View File

@@ -51,7 +51,7 @@ export function BribeGame(props: IMinigameProps): React.ReactElement {
<GameTimer millis={timer} onExpire={props.onFailure} />
<Grid item xs={12}>
<h1>Say something nice about the guard.</h1>
<KeyHandler onKeyDown={press} />
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
<Grid item xs={6}>
<h2 style={{fontSize: "2em"}}></h2>

View File

@@ -47,7 +47,7 @@ export function CheatCodeGame(props: IMinigameProps): React.ReactElement {
<Grid item xs={12}>
<h1 className={"noselect"}>Enter the Code!</h1>
<p style={{fontSize: '5em'}}>{code[index]}</p>
<KeyHandler onKeyDown={press} />
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>)
}

View File

@@ -86,7 +86,7 @@ export function Cyberpunk2077Game(props: IMinigameProps): React.ReactElement {
return <span key={`${x}${y}`} style={{fontSize: fontSize, color: 'blue'}}>{cell}&nbsp;</span>
return <span key={`${x}${y}`} style={{fontSize: fontSize}}>{cell}&nbsp;</span>
})}</pre><br /></div>)}
<KeyHandler onKeyDown={press} />
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>)
}

View File

@@ -85,10 +85,13 @@ export function Game(props: IProps): React.ReactElement {
})
}
function failure(): void {
function failure(options?: { automated: boolean }): void {
setStage(Stage.Countdown);
pushResult(false);
if(props.Player.takeDamage(props.StartingDifficulty*3)) {
// Kill the player immediately if they use automation, so
// it's clear they're not meant to
const damage = options?.automated ? props.Player.hp : props.StartingDifficulty*3;
if(props.Player.takeDamage(damage)) {
const menu = document.getElementById("mainmenu-container");
if(menu === null) throw new Error("mainmenu-container not found");
menu.style.visibility = "visible";

View File

@@ -1,5 +1,8 @@
export interface IMinigameProps {
onSuccess: () => void;
onFailure: () => void;
onFailure: (options?: {
/** Failed due to using untrusted events (automation) */
automated: boolean;
}) => void;
difficulty: number;
}

View File

@@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
interface IProps {
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => void;
onFailure: (options?: { automated: boolean }) => void;
}
export function KeyHandler(props: IProps): React.ReactElement {
@@ -9,7 +10,12 @@ export function KeyHandler(props: IProps): React.ReactElement {
useEffect(() => elem.focus());
function onKeyDown(event: React.KeyboardEvent<HTMLElement>): void {
if(!event.isTrusted) return;
console.log("isTrusted?", event.isTrusted)
if(!event.isTrusted) {
console.log("untrusted event!")
props.onFailure({ automated: true });
return;
}
props.onKeyDown(event);
}

View File

@@ -94,7 +94,7 @@ export function MinesweeperGame(props: IMinigameProps): React.ReactElement {
return <span key={x}>[&nbsp;]&nbsp;</span>
}
})}</pre><br /></div>)}
<KeyHandler onKeyDown={press} />
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>)
}

View File

@@ -39,9 +39,9 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
useEffect(() => {
let id2 = -1;
const id = setTimeout(() => {
const id = window.setTimeout(() => {
setGuarding(false);
id2 = setTimeout(()=>setGuarding(true), difficulty.window)
id2 = window.setTimeout(()=>setGuarding(true), difficulty.window)
}, Math.random()*3250+1500);
return () => {
clearInterval(id);
@@ -54,7 +54,7 @@ export function SlashGame(props: IMinigameProps): React.ReactElement {
<Grid item xs={12}>
<h1 className={"noselect"}>Slash when his guard is down!</h1>
<p style={{fontSize: '5em'}}>{guarding ? "!Guarding!" : "!ATTACKING!"}</p>
<KeyHandler onKeyDown={press} />
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>)
}

View File

@@ -111,7 +111,7 @@ export function WireCuttingGame(props: IMinigameProps): React.ReactElement {
})}
</pre>
</div>)}
<KeyHandler onKeyDown={press} />
<KeyHandler onKeyDown={press} onFailure={props.onFailure} />
</Grid>
</Grid>)
}

View File

@@ -936,6 +936,9 @@ function NetscriptFunctions(workerScript) {
}
post(`${workerScript.scriptRef.filename}: ${argsToString(arguments)}`);
},
tprintf: function(format, ...args) {
post(vsprintf(format, args));
},
clearLog: function() {
workerScript.scriptRef.clearLog();
},
@@ -3029,6 +3032,7 @@ function NetscriptFunctions(workerScript) {
has4SDataTixApi: Player.has4SDataTixApi,
bladeburner_max_stamina_mult: Player.bladeburner_max_stamina_mult,
bladeburner_stamina_gain_mult: Player.bladeburner_stamina_gain_mult,
bladeburner_analysis_mult: Player.bladeburner_analysis_mult,
bladeburner_success_chance_mult: Player.bladeburner_success_chance_mult,
bitNodeN: Player.bitNodeN,
totalPlaytime: Player.totalPlaytime,
@@ -4065,7 +4069,7 @@ function NetscriptFunctions(workerScript) {
return true; // Already member
} else if (Player.strength >= 100 && Player.defense >= 100 &&
Player.dexterity >= 100 && Player.agility >= 100) {
Player.bladeburner = new Bladeburner();
Player.bladeburner = new Bladeburner(Player);
workerScript.log("joinBladeburnerDivision", "You have been accepted into the Bladeburner division");
const worldHeader = document.getElementById("world-menu-header");
@@ -4546,4 +4550,4 @@ function NetscriptFunctions(workerScript) {
return functions;
} // End NetscriptFunction()
export { NetscriptFunctions };
export { NetscriptFunctions };

View File

@@ -82,7 +82,6 @@ BitburnerSaveObject.prototype.getSaveString = function() {
this.GlobalAliasesSave = JSON.stringify(GlobalAliases);
this.MessagesSave = JSON.stringify(Messages);
this.StockMarketSave = JSON.stringify(StockMarket);
console.log(JSON.stringify(Settings));
this.SettingsSave = JSON.stringify(Settings);
this.FconfSettingsSave = JSON.stringify(FconfSettings);
this.VersionSave = JSON.stringify(CONSTANTS.Version);

View File

@@ -18,8 +18,8 @@ export class PositionTracker {
const position = this.positions.get(filename);
if (!position) {
return {
row: 0,
column: 0,
row: -1,
column: -1,
};
}
return position;

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect, useRef } from 'react';
import { StdButton } from "../../ui/React/StdButton";
import Editor from "@monaco-editor/react";
import * as monaco from "monaco-editor";
import IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
import { createPopup } from "../../ui/React/createPopup";
import { OptionsPopup } from "./OptionsPopup";
import { Options } from "./Options";
@@ -23,6 +23,7 @@ import { libSource } from "../NetscriptDefinitions";
import { NetscriptFunctions } from "../../NetscriptFunctions";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { Settings } from "../../Settings/Settings";
import { GetServerByHostname } from "../../Server/ServerHelpers";
import {
iTutorialNextStep,
ITutorial,
@@ -55,7 +56,7 @@ interface IProps {
code: string;
player: IPlayer;
engine: IEngine;
};
}
/*
@@ -69,16 +70,30 @@ interface IProps {
// https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
// https://github.com/threehams/typescript-error-guide/blob/master/stories/components/Editor.tsx#L11-L39
// These variables are used to reload a script when it's clicked on. Because we
// won't have references to the old script.
let lastFilename = "";
let lastCode = "";
let lastPosition: monaco.Position | null = null;
export function Root(props: IProps): React.ReactElement {
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
const [filename, setFilename] = useState(props.filename);
const [code, setCode] = useState<string>(props.code);
const [ram, setRAM] = useState('');
const [filename, setFilename] = useState(props.filename ? props.filename : lastFilename);
const [code, setCode] = useState<string>(props.code ? props.code : lastCode);
const [ram, setRAM] = useState('RAM: ???');
const [options, setOptions] = useState<Options>({
theme: Settings.MonacoTheme,
insertSpaces: Settings.MonacoInsertSpaces,
});
// store the last known state in case we need to restart without nano.
useEffect(() => {
if(props.filename === "") return;
lastFilename = props.filename;
lastCode = props.code;
lastPosition = null;
}, []);
function save(): void {
if(editorRef.current !== null) {
const position = editorRef.current.getPosition();
@@ -89,6 +104,7 @@ export function Root(props: IProps): React.ReactElement {
});
}
}
lastPosition = null;
// TODO(hydroflame): re-enable the tutorial.
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
@@ -103,19 +119,20 @@ export function Root(props: IProps): React.ReactElement {
}
//Save the script
const s = props.player.getCurrentServer();
for (let i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript(code, props.player.currentServer, props.player.getCurrentServer().scripts);
const server = props.player.getCurrentServer();
if(server === null) throw new Error('Server should not be null but it is.');
for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) {
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
props.engine.loadTerminalContent();
return iTutorialNextStep();
}
}
// If the current script does NOT exist, create a new one
let script = new Script();
script.saveScript(code, props.player.currentServer, props.player.getCurrentServer().scripts);
s.scripts.push(script);
const script = new Script();
script.saveScript(code, props.player.currentServer, server.scripts);
server.scripts.push(script);
return iTutorialNextStep();
}
@@ -130,7 +147,8 @@ export function Root(props: IProps): React.ReactElement {
return;
}
const s = props.player.getCurrentServer();
const server = props.player.getCurrentServer();
if(server === null) throw new Error('Server should not be null but it is.');
if (filename === ".fconf") {
try {
parseFconfSettings(code);
@@ -140,9 +158,9 @@ export function Root(props: IProps): React.ReactElement {
}
} else if (isScriptFilename(filename)) {
//If the current script already exists on the server, overwrite it
for (let i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript(code, props.player.currentServer, props.player.getCurrentServer().scripts);
for (let i = 0; i < server.scripts.length; i++) {
if (filename == server.scripts[i].filename) {
server.scripts[i].saveScript(code, props.player.currentServer, server.scripts);
props.engine.loadTerminalContent();
return;
}
@@ -150,18 +168,18 @@ export function Root(props: IProps): React.ReactElement {
//If the current script does NOT exist, create a new one
const script = new Script();
script.saveScript(code, props.player.currentServer, props.player.getCurrentServer().scripts);
s.scripts.push(script);
script.saveScript(code, props.player.currentServer, server.scripts);
server.scripts.push(script);
} else if (filename.endsWith(".txt")) {
for (let i = 0; i < s.textFiles.length; ++i) {
if (s.textFiles[i].fn === filename) {
s.textFiles[i].write(code);
for (let i = 0; i < server.textFiles.length; ++i) {
if (server.textFiles[i].fn === filename) {
server.textFiles[i].write(code);
props.engine.loadTerminalContent();
return;
}
}
const textFile = new TextFile(filename, code);
s.textFiles.push(textFile);
server.textFiles.push(textFile);
} else {
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " +
" or text file (.txt)")
@@ -171,14 +189,17 @@ export function Root(props: IProps): React.ReactElement {
}
function beautify(): void {
setCode(code => beautifyCode(code, {
if (editorRef.current === null) return;
const pretty = beautifyCode(code, {
indent_with_tabs: !options.insertSpaces,
indent_size: 4,
brace_style: "preserve-inline",
}));
});
editorRef.current.setValue(pretty);
}
function onFilenameChange(event: React.ChangeEvent<HTMLInputElement>): void {
lastFilename = filename;
setFilename(event.target.value);
}
@@ -196,12 +217,16 @@ export function Root(props: IProps): React.ReactElement {
setOptions(options);
Settings.MonacoTheme = options.theme;
Settings.MonacoInsertSpaces = options.insertSpaces;
}
},
});
}
function updateCode(newCode?: string): void {
if(newCode === undefined) return;
lastCode = newCode;
if(editorRef.current !== null) {
lastPosition = editorRef.current.getPosition();
}
setCode(newCode);
}
@@ -252,7 +277,10 @@ export function Root(props: IProps): React.ReactElement {
editorRef.current = editor;
if(editorRef.current === null) return;
const position = CursorPositions.getCursor(filename);
editorRef.current.setPosition({lineNumber: position.row, column: position.column});
if(position.row !== -1)
editorRef.current.setPosition({lineNumber: position.row, column: position.column});
else if(lastPosition !== null)
editorRef.current.setPosition({lineNumber: lastPosition.lineNumber, column: lastPosition.column+1});
editorRef.current.focus();
}
@@ -269,7 +297,7 @@ export function Root(props: IProps): React.ReactElement {
});
}
return { suggestions: suggestions };
}
},
});
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, 'netscript.d.ts');
monaco.languages.typescript.typescriptDefaults.addExtraLib(libSource, 'netscript.d.ts');
@@ -288,7 +316,6 @@ export function Root(props: IProps): React.ReactElement {
height="80%"
defaultLanguage="javascript"
defaultValue={code}
value={code}
onChange={updateCode}
theme={options.theme}
options={options}
@@ -300,4 +327,4 @@ export function Root(props: IProps): React.ReactElement {
<a className="std-button" style={{display: "inline-block"}} target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">Netscript Documentation</a>
</div>
</div>);
}
}

View File

@@ -57,6 +57,7 @@ import {
loadAllRunningScripts,
updateOnlineScriptTimes,
} from "./NetscriptWorker";
import { GetServerByHostname } from "./Server/ServerHelpers";
import { Player } from "./Player";
import { prestigeAugmentation } from "./Prestige";
import {
@@ -101,6 +102,9 @@ import { ActiveScriptsRoot } from "./ui/ActiveScripts/Root";
import { initializeMainMenuHeaders } from "./ui/MainMenu/Headers";
import { initializeMainMenuLinks, MainMenuLinks } from "./ui/MainMenu/Links";
import { FileDiagnosticPopup } from "./Diagnostic/FileDiagnosticPopup";
import { createPopup } from "./ui/React/createPopup";
import { dialogBoxCreate } from "../utils/DialogBox";
import { gameOptionsBoxClose, gameOptionsBoxOpen } from "../utils/GameOptions";
import { exceptionAlert } from "../utils/helpers/exceptionAlert";
@@ -251,9 +255,6 @@ const Engine = {
Engine.Display.scriptEditorContent.style.display = "block";
routing.navigateTo(Page.ScriptEditor);
const monaco = document.getElementById('monaco-editor');
//https://www.npmjs.com/package/@monaco-editor/react#development-playground
ReactDOM.render(
<ScriptEditorRoot filename={filename} code={code} player={Player} engine={this} />,
Engine.Display.scriptEditorContent,
@@ -1525,6 +1526,12 @@ const Engine = {
gameOptionsBoxClose();
return false;
});
// DEBUG File diagnostic
document.getElementById("debug-files").addEventListener("click", function() {
createPopup("debug-files-diagnostic-popup", FileDiagnosticPopup, {});
return false;
});
},
start: function() {
@@ -1573,4 +1580,4 @@ window.onload = function() {
}
};
export {Engine, indexedDb};
export {Engine};

View File

@@ -594,10 +594,12 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
Copy Save data to Clipboard
</button>
<button id="debug-delete-scripts-link" class="a-link-button tooltip">
Delete all active scripts
Force kill all active scripts
<span class="tooltiptextleft">
Forcefully kill all active running scripts, in case there is a bug or some unexpected issue with the game. After
using this, save the game and then reload the page.
using this, save the game and then reload the page. This is different then normal kill in that normal kill
will tell the script to shut down while force kill just removes the references to it (and it should crash on it's own).
This will not remove the files on your computer. Just forcefully kill all running instance of all scripts.
</span>
</button>
<button id="debug-soft-reset" class="a-link-button tooltip">
@@ -606,6 +608,14 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
Perform a soft reset. Resets everything as if you had just purchased an Augmentation.
</span>
</button>
<button id="debug-files" class="a-link-button tooltip">
Diagnose files
<span class="tooltiptextleft">
If your save file is extremely big you can use this button
to view a map of all the files on every server. Be careful
there might be spoilers.
</span>
</button>
</div>
</div>
</div>

View File

@@ -6,6 +6,7 @@ import * as React from "react";
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
import { Accordion } from "../React/Accordion";
import { ServerAccordionContent } from "./ServerAccordionContent";
import { BaseServer } from "../../Server/BaseServer";
import { WorkerScript } from "../../Netscript/WorkerScript";
@@ -42,7 +43,7 @@ export function ServerAccordion(props: IProps): React.ReactElement {
<pre>{headerTxt}</pre>
}
panelContent={
<ul>{scripts}</ul>
<ServerAccordionContent workerScripts={props.workerScripts} />
}
/>
)

View File

@@ -0,0 +1,83 @@
import React, { useState } from "react";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { WorkerScriptAccordion } from "./WorkerScriptAccordion";
import { AccordionButton } from "../React/AccordionButton";
const pageSize = 20;
interface IProps {
workerScripts: WorkerScript[];
}
export function ServerAccordionContent(props: IProps): React.ReactElement {
if(props.workerScripts.length > pageSize) {
return <ServerAccordionContentPaginated workerScripts={props.workerScripts} />
}
const scripts = props.workerScripts.map((ws) => {
return (
<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />
)
});
return (<ul>{scripts}</ul>);
}
export function ServerAccordionContentPaginated(props: IProps): React.ReactElement {
const [page, setPage] = useState(0);
const scripts: React.ReactElement[] = [];
const maxPage = Math.ceil(props.workerScripts.length/pageSize);
const maxScript = Math.min((page+1)*pageSize, props.workerScripts.length);
for(let i = page*pageSize; i < maxScript; i++) {
const ws = props.workerScripts[i];
scripts.push(<WorkerScriptAccordion key={`${ws.name}_${ws.args}`} workerScript={ws} />)
}
function capPage(page: number): number {
if(page < 0) {
page = 0;
}
if(maxPage-1 < page) {
page = maxPage-1;
}
return page;
}
// in case we're on an invalid page number because scripts were killed.
const capped = capPage(page);
if(capped !== page)
setPage(capped);
function changePage(n: number): void {
setPage(newPage => {
newPage += n;
newPage = Math.round(newPage);
return capPage(newPage);
})
}
return (<><ul>{scripts}</ul>
<AccordionButton
onClick={() => changePage(-1e99)}
text="<<"
/>
<AccordionButton
onClick={() => changePage(-1)}
text="<"
/>
<span className="text">{page+1} / {maxPage}</span>
<AccordionButton
onClick={() => changePage(1)}
text=">"
/>
<AccordionButton
onClick={() => changePage(1e99)}
text=">>"
/>
</>);
}

View File

@@ -73,9 +73,11 @@ class AccordionPanel extends React.Component<IPanelProps, any> {
className = this.props.panelClass;
}
if(!this.props.opened) return (<></>);
return (
<div className={className} style={{display: this.props.opened ? "block" : "none"}}>
<div className={className} style={{display: "block"}}>
{this.props.panelContent}
</div>
)

View File

@@ -31,7 +31,7 @@ export class AutoupdatingParagraph extends React.Component<IProps, IState> {
componentDidMount(): void {
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
this.interval = setInterval(() => this.tick(), time);
this.interval = window.setInterval(() => this.tick(), time);
}
componentWillUnmount(): void {

View File

@@ -38,7 +38,7 @@ export class AutoupdatingStdButton extends React.Component<IProps, IState> {
componentDidMount(): void {
const time = this.props.intervalTime ? this.props.intervalTime : 1000;
this.interval = setInterval(() => this.tick(), time);
this.interval = window.setInterval(() => this.tick(), time);
}
componentWillUnmount(): void {

View File

@@ -5,12 +5,6 @@ import { RunningScript } from "../../src/Script/RunningScript";
import { Script } from "../../src/Script/Script";
import { SourceFileFlags } from "../../src/SourceFile/SourceFileFlags";
import { expect } from "chai";
console.log("Beginning Netscript Dynamic RAM Calculation/Generation Tests");
console.log("Beginning Netscript Static RAM Calculation/Generation Tests");
const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost;
describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
@@ -27,12 +21,13 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
// Tests numeric equality, allowing for floating point imprecision
function testEquality(val, expected) {
expect(val).to.be.within(expected - 100 * Number.EPSILON, expected + 100 * Number.EPSILON);
expect(val).toBeGreaterThanOrEqual(expected - 100 * Number.EPSILON);
expect(val).toBeLessThanOrEqual(expected + 100 * Number.EPSILON);
}
// Runs a Netscript function and properly catches it if it returns promise
function runPotentiallyAsyncFunction(fn) {
let res = fn();
const res = fn();
if (res instanceof Promise) {
res.catch(() => undefined);
}
@@ -47,9 +42,9 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
* including the namespace(s). e.g. ["gang", "getMemberNames"]
*/
async function testNonzeroDynamicRamCost(fnDesc) {
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to testNonzeroDynamicRamCost()"); }
if (!Array.isArray(fnDesc)) { throw new Error("Non-array passed to testNonzeroDynamicRamCost()"); }
const expected = getRamCost(...fnDesc);
expect(expected).to.be.above(0);
expect(expected).toBeGreaterThan(0);
const code = `${fnDesc.join(".")}();`
@@ -72,7 +67,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
let curr = scope[fnDesc[0]];
for (let i = 1; i < fnDesc.length; ++i) {
if (curr == null) {
expect.fail(`Invalid function specified: [${fnDesc}]`);
throw new Error(`Invalid function specified: [${fnDesc}]`);
}
if (typeof curr === "function") {
@@ -92,13 +87,13 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
runPotentiallyAsyncFunction(curr);
} catch(e) {}
} else {
expect.fail(`Invalid function specified: [${fndesc}]`);
throw new Error(`Invalid function specified: [${fnDesc}]`);
}
const fnName = fnDesc[fnDesc.length - 1];
testEquality(workerScript.dynamicRamUsage - ScriptBaseCost, expected);
testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage);
expect(workerScript.dynamicLoadedFns).to.have.property(fnName);
expect(workerScript.dynamicLoadedFns).toHaveProperty(fnName);
}
/**
@@ -110,9 +105,9 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
* including the namespace(s). e.g. ["gang", "getMemberNames"]
*/
async function testZeroDynamicRamCost(fnDesc) {
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to testZeroDynamicRamCost()"); }
if (!Array.isArray(fnDesc)) { throw new Error("Non-array passed to testZeroDynamicRamCost()"); }
const expected = getRamCost(...fnDesc);
expect(expected).to.equal(0);
expect(expected).toEqual(0);
const code = `${fnDesc.join(".")}();`
@@ -135,7 +130,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
let curr = scope[fnDesc[0]];
for (let i = 1; i < fnDesc.length; ++i) {
if (curr == null) {
expect.fail(`Invalid function specified: [${fnDesc}]`);
throw new Error(`Invalid function specified: [${fnDesc}]`);
}
if (typeof curr === "function") {
@@ -155,20 +150,20 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
runPotentiallyAsyncFunction(curr);
} catch(e) {}
} else {
expect.fail(`Invalid function specified: [${fndesc}]`);
throw new Error(`Invalid function specified: [${fndesc}]`);
}
testEquality(workerScript.dynamicRamUsage, ScriptBaseCost);
testEquality(workerScript.dynamicRamUsage, runningScript.ramUsage);
}
before(function() {
beforeEach(function() {
for (let i = 0; i < SourceFileFlags.length; ++i) {
SourceFileFlags[i] = 3;
}
});
describe("Basic Functions", async function() {
describe("Basic Functions", function() {
it("hack()", async function() {
const f = ["hack"];
await testNonzeroDynamicRamCost(f);
@@ -570,14 +565,14 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
});
});
describe("Advanced Functions", async function() {
describe("Advanced Functions", function() {
it("getBitNodeMultipliers()", async function() {
const f = ["getBitNodeMultipliers"];
await testNonzeroDynamicRamCost(f);
});
});
describe("TIX API", async function() {
describe("TIX API", function() {
it("getStockSymbols()", async function() {
const f = ["getStockSymbols"];
await testNonzeroDynamicRamCost(f);
@@ -664,7 +659,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
});
});
describe("Singularity Functions", async function() {
describe("Singularity Functions", function() {
it("universityCourse()", async function() {
const f = ["universityCourse"];
await testNonzeroDynamicRamCost(f);
@@ -831,7 +826,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
});
});
describe("Bladeburner API", async function() {
describe("Bladeburner API", function() {
it("getContractNames()", async function() {
const f = ["bladeburner", "getContractNames"];
await testNonzeroDynamicRamCost(f);
@@ -1003,7 +998,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
});
});
describe("Gang API", async function() {
describe("Gang API", function() {
it("getMemberNames()", async function() {
const f = ["gang", "getMemberNames"];
await testNonzeroDynamicRamCost(f);
@@ -1085,7 +1080,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
});
});
describe("Coding Contract API", async function() {
describe("Coding Contract API", function() {
it("attempt()", async function() {
const f = ["codingcontract", "attempt"];
await testNonzeroDynamicRamCost(f);
@@ -1112,7 +1107,7 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
});
});
describe("Sleeve API", async function() {
describe("Sleeve API", function() {
it("getNumSleeves()", async function() {
const f = ["sleeve", "getNumSleeves"];
await testNonzeroDynamicRamCost(f);

View File

@@ -1,8 +1,5 @@
import { getRamCost, RamCostConstants } from "../../src/Netscript/RamCostGenerator";
import { calculateRamUsage } from "../../src/Script/RamCalculations"
import { expect } from "chai";
console.log("Beginning Netscript Static RAM Calculation/Generation Tests");
const ScriptBaseCost = RamCostConstants.ScriptBaseRamCost;
const HacknetNamespaceCost = RamCostConstants.ScriptHacknetNodesRamCost;
@@ -10,7 +7,8 @@ const HacknetNamespaceCost = RamCostConstants.ScriptHacknetNodesRamCost;
describe("Netscript Static RAM Calculation/Generation Tests", function() {
// Tests numeric equality, allowing for floating point imprecision
function testEquality(val, expected) {
expect(val).to.be.within(expected - 100 * Number.EPSILON, expected + 100 * Number.EPSILON);
expect(val).toBeGreaterThanOrEqual(expected - 100 * Number.EPSILON);
expect(val).toBeLessThanOrEqual(expected + 100 * Number.EPSILON);
}
/**
@@ -24,7 +22,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
async function expectNonZeroRamCost(fnDesc) {
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to expectNonZeroRamCost()"); }
const expected = getRamCost(...fnDesc);
expect(expected).to.be.above(0);
expect(expected).toBeGreaterThan(0);
const code = fnDesc.join(".") + "(); ";
@@ -33,7 +31,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
const multipleCallsCode = code.repeat(3);
const multipleCallsCalculated = await calculateRamUsage(multipleCallsCode, []);
expect(multipleCallsCalculated).to.equal(calculated);
expect(multipleCallsCalculated).toEqual(calculated);
}
/**
@@ -47,7 +45,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
async function expectZeroRamCost(fnDesc) {
if (!Array.isArray(fnDesc)) { expect.fail("Non-array passed to expectZeroRamCost()"); }
const expected = getRamCost(...fnDesc);
expect(expected).to.equal(0);
expect(expected).toEqual(0);
const code = fnDesc.join(".") + "(); ";
@@ -55,10 +53,10 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
testEquality(calculated, ScriptBaseCost);
const multipleCallsCalculated = await calculateRamUsage(code, []);
expect(multipleCallsCalculated).to.equal(ScriptBaseCost);
expect(multipleCallsCalculated).toEqual(ScriptBaseCost);
}
describe("Basic Functions", async function() {
describe("Basic Functions", function() {
it("hack()", async function() {
const f = ["hack"];
await expectNonZeroRamCost(f);
@@ -460,14 +458,14 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
});
});
describe("Advanced Functions", async function() {
describe("Advanced Functions", function() {
it("getBitNodeMultipliers()", async function() {
const f = ["getBitNodeMultipliers"];
await expectNonZeroRamCost(f);
});
});
describe("Hacknet Node API", async function() {
describe("Hacknet Node API", function() {
// The Hacknet Node API RAM cost is a bit different because
// it's just a one-time cost to access the 'hacknet' namespace.
// Otherwise, all functions cost 0 RAM
@@ -490,7 +488,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
]
it("should have zero RAM cost for all functions", function() {
for (const fn of apiFunctions) {
expect(getRamCost("hacknet", fn)).to.equal(0);
expect(getRamCost("hacknet", fn)).toEqual(0);
}
});
@@ -505,7 +503,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
});
});
describe("TIX API", async function() {
describe("TIX API", function() {
it("getStockSymbols()", async function() {
const f = ["getStockSymbols"];
await expectNonZeroRamCost(f);
@@ -602,7 +600,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
});
});
describe("Singularity Functions", async function() {
describe("Singularity Functions", function() {
it("universityCourse()", async function() {
const f = ["universityCourse"];
await expectNonZeroRamCost(f);
@@ -769,7 +767,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
});
});
describe("Bladeburner API", async function() {
describe("Bladeburner API", function() {
it("getContractNames()", async function() {
const f = ["bladeburner", "getContractNames"];
await expectNonZeroRamCost(f);
@@ -941,7 +939,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
});
});
describe("Gang API", async function() {
describe("Gang API", function() {
it("getMemberNames()", async function() {
const f = ["gang", "getMemberNames"];
await expectNonZeroRamCost(f);
@@ -1023,7 +1021,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
});
});
describe("Coding Contract API", async function() {
describe("Coding Contract API", function() {
it("attempt()", async function() {
const f = ["codingcontract", "attempt"];
await expectNonZeroRamCost(f);
@@ -1050,7 +1048,7 @@ describe("Netscript Static RAM Calculation/Generation Tests", function() {
});
});
describe("Sleeve API", async function() {
describe("Sleeve API", function() {
it("getNumSleeves()", async function() {
const f = ["sleeve", "getNumSleeves"];
await expectNonZeroRamCost(f);

View File

@@ -1,5 +1,10 @@
# Unit Tests
This directory contains unit tests for Bitburner.
Unit tests use Mocha/Chai and are run using mochapack (a mocha-webpack fork).
Run the test command with `npm run test`
Unit tests use jest.
## Running
Run tests with: `npm run test`
To watch for changes: `npm run test:watch`

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,33 @@
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
describe("StringHelperFunctions Tests", function () {
it("transforms strings", () => {
expect(convertTimeMsToTimeElapsedString(1000)).toEqual("1 seconds");
expect(convertTimeMsToTimeElapsedString(5 * 60 * 1000 + 34 * 1000)).toEqual(
"5 minutes 34 seconds",
);
expect(
convertTimeMsToTimeElapsedString(
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000,
),
).toEqual("2 days 5 minutes 34 seconds");
expect(
convertTimeMsToTimeElapsedString(
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000,
true,
),
).toEqual("2 days 5 minutes 34.000 seconds");
expect(
convertTimeMsToTimeElapsedString(
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000 + 123,
true,
),
).toEqual("2 days 5 minutes 34.123 seconds");
expect(
convertTimeMsToTimeElapsedString(
2 * 60 * 60 * 24 * 1000 + 5 * 60 * 1000 + 34 * 1000 + 123.888,
true,
),
).toEqual("2 days 5 minutes 34.123 seconds");
})
});

View File

@@ -1,11 +0,0 @@
import { expect } from "chai";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
describe("StringHelperFunctions Tests", function() {
expect(convertTimeMsToTimeElapsedString(1000)).to.equal("1 seconds");
expect(convertTimeMsToTimeElapsedString(5*60*1000+34*1000)).to.equal("5 minutes 34 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000)).to.equal("2 days 5 minutes 34 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000, true)).to.equal("2 days 5 minutes 34.000 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000+123, true)).to.equal("2 days 5 minutes 34.123 seconds");
expect(convertTimeMsToTimeElapsedString(2*60*60*24*1000+5*60*1000+34*1000+123.888, true)).to.equal("2 days 5 minutes 34.123 seconds");
})

View File

@@ -0,0 +1,295 @@
import * as dirHelpers from "../../src/Terminal/DirectoryHelpers";
describe("Terminal Directory Tests", function() {
describe("removeLeadingSlash()", function() {
const removeLeadingSlash = dirHelpers.removeLeadingSlash;
it("should remove first slash in a string", function() {
expect(removeLeadingSlash("/")).toEqual("");
expect(removeLeadingSlash("/foo.txt")).toEqual("foo.txt");
expect(removeLeadingSlash("/foo/file.txt")).toEqual("foo/file.txt");
});
it("should only remove one slash", function() {
expect(removeLeadingSlash("///")).toEqual("//");
expect(removeLeadingSlash("//foo")).toEqual("/foo");
});
it("should do nothing for a string that doesn't start with a slash", function() {
expect(removeLeadingSlash("foo.txt")).toEqual("foo.txt");
expect(removeLeadingSlash("foo/test.txt")).toEqual("foo/test.txt");
});
it("should not fail on an empty string", function() {
expect(removeLeadingSlash.bind(null, "")).not.toThrow();
expect(removeLeadingSlash("")).toEqual("");
});
});
describe("removeTrailingSlash()", function() {
const removeTrailingSlash = dirHelpers.removeTrailingSlash;
it("should remove last slash in a string", function() {
expect(removeTrailingSlash("/")).toEqual("");
expect(removeTrailingSlash("foo.txt/")).toEqual("foo.txt");
expect(removeTrailingSlash("foo/file.txt/")).toEqual("foo/file.txt");
});
it("should only remove one slash", function() {
expect(removeTrailingSlash("///")).toEqual("//");
expect(removeTrailingSlash("foo//")).toEqual("foo/");
});
it("should do nothing for a string that doesn't end with a slash", function() {
expect(removeTrailingSlash("foo.txt")).toEqual("foo.txt");
expect(removeTrailingSlash("foo/test.txt")).toEqual("foo/test.txt");
});
it("should not fail on an empty string", function() {
expect(removeTrailingSlash.bind(null, "")).not.toThrow();
expect(removeTrailingSlash("")).toEqual("");
});
});
describe("isValidFilename()", function() {
const isValidFilename = dirHelpers.isValidFilename;
it("should return true for valid filenames", function() {
expect(isValidFilename("test.txt")).toEqual(true);
expect(isValidFilename("123.script")).toEqual(true);
expect(isValidFilename("foo123.b")).toEqual(true);
expect(isValidFilename("my_script.script")).toEqual(true);
expect(isValidFilename("my-script.script")).toEqual(true);
expect(isValidFilename("_foo.lit")).toEqual(true);
expect(isValidFilename("mult.periods.script")).toEqual(true);
expect(isValidFilename("mult.per-iods.again.script")).toEqual(true);
expect(isValidFilename("BruteSSH.exe-50%-INC")).toEqual(true);
expect(isValidFilename("DeepscanV1.exe-1.01%-INC")).toEqual(true);
expect(isValidFilename("DeepscanV2.exe-1.00%-INC")).toEqual(true);
expect(isValidFilename("AutoLink.exe-1.%-INC")).toEqual(true);
});
it("should return false for invalid filenames", function() {
expect(isValidFilename("foo")).toEqual(false);
expect(isValidFilename("my script.script")).toEqual(false);
expect(isValidFilename("a^.txt")).toEqual(false);
expect(isValidFilename("b#.lit")).toEqual(false);
expect(isValidFilename("lib().js")).toEqual(false);
expect(isValidFilename("foo.script_")).toEqual(false);
expect(isValidFilename("foo._script")).toEqual(false);
expect(isValidFilename("foo.hyphened-ext")).toEqual(false);
expect(isValidFilename("")).toEqual(false);
expect(isValidFilename("AutoLink-1.%-INC.exe")).toEqual(false);
expect(isValidFilename("AutoLink.exe-1.%-INC.exe")).toEqual(false);
expect(isValidFilename("foo%.exe")).toEqual(false);
expect(isValidFilename("-1.00%-INC")).toEqual(false);
});
});
describe("isValidDirectoryName()", function() {
const isValidDirectoryName = dirHelpers.isValidDirectoryName;
it("should return true for valid directory names", function() {
expect(isValidDirectoryName("a")).toEqual(true);
expect(isValidDirectoryName("foo")).toEqual(true);
expect(isValidDirectoryName("foo-dir")).toEqual(true);
expect(isValidDirectoryName("foo_dir")).toEqual(true);
expect(isValidDirectoryName(".a")).toEqual(true);
expect(isValidDirectoryName("1")).toEqual(true);
expect(isValidDirectoryName("a1")).toEqual(true);
expect(isValidDirectoryName(".a1")).toEqual(true);
expect(isValidDirectoryName("._foo")).toEqual(true);
expect(isValidDirectoryName("_foo")).toEqual(true);
});
it("should return false for invalid directory names", function() {
expect(isValidDirectoryName("")).toEqual(false);
expect(isValidDirectoryName("foo.dir")).toEqual(false);
expect(isValidDirectoryName("1.")).toEqual(false);
expect(isValidDirectoryName("foo.")).toEqual(false);
expect(isValidDirectoryName("dir#")).toEqual(false);
expect(isValidDirectoryName("dir!")).toEqual(false);
expect(isValidDirectoryName("dir*")).toEqual(false);
expect(isValidDirectoryName(".")).toEqual(false);
});
});
describe("isValidDirectoryPath()", function() {
const isValidDirectoryPath = dirHelpers.isValidDirectoryPath;
it("should return false for empty strings", function() {
expect(isValidDirectoryPath("")).toEqual(false);
});
it("should return true only for the forward slash if the string has length 1", function() {
expect(isValidDirectoryPath("/")).toEqual(true);
expect(isValidDirectoryPath(" ")).toEqual(false);
expect(isValidDirectoryPath(".")).toEqual(false);
expect(isValidDirectoryPath("a")).toEqual(false);
});
it("should return true for valid directory paths", function() {
expect(isValidDirectoryPath("/a")).toEqual(true);
expect(isValidDirectoryPath("/dir/a")).toEqual(true);
expect(isValidDirectoryPath("/dir/foo")).toEqual(true);
expect(isValidDirectoryPath("/.dir/foo-dir")).toEqual(true);
expect(isValidDirectoryPath("/.dir/foo_dir")).toEqual(true);
expect(isValidDirectoryPath("/.dir/.a")).toEqual(true);
expect(isValidDirectoryPath("/dir1/1")).toEqual(true);
expect(isValidDirectoryPath("/dir1/a1")).toEqual(true);
expect(isValidDirectoryPath("/dir1/.a1")).toEqual(true);
expect(isValidDirectoryPath("/dir_/._foo")).toEqual(true);
expect(isValidDirectoryPath("/dir-/_foo")).toEqual(true);
});
it("should return false if the path does not have a leading slash", function() {
expect(isValidDirectoryPath("a")).toEqual(false);
expect(isValidDirectoryPath("dir/a")).toEqual(false);
expect(isValidDirectoryPath("dir/foo")).toEqual(false);
expect(isValidDirectoryPath(".dir/foo-dir")).toEqual(false);
expect(isValidDirectoryPath(".dir/foo_dir")).toEqual(false);
expect(isValidDirectoryPath(".dir/.a")).toEqual(false);
expect(isValidDirectoryPath("dir1/1")).toEqual(false);
expect(isValidDirectoryPath("dir1/a1")).toEqual(false);
expect(isValidDirectoryPath("dir1/.a1")).toEqual(false);
expect(isValidDirectoryPath("dir_/._foo")).toEqual(false);
expect(isValidDirectoryPath("dir-/_foo")).toEqual(false);
});
it("should accept dot notation", function() {
expect(isValidDirectoryPath("/dir/./a")).toEqual(true);
expect(isValidDirectoryPath("/dir/../foo")).toEqual(true);
expect(isValidDirectoryPath("/.dir/./foo-dir")).toEqual(true);
expect(isValidDirectoryPath("/.dir/../foo_dir")).toEqual(true);
expect(isValidDirectoryPath("/.dir/./.a")).toEqual(true);
expect(isValidDirectoryPath("/dir1/1/.")).toEqual(true);
expect(isValidDirectoryPath("/dir1/a1/..")).toEqual(true);
expect(isValidDirectoryPath("/dir1/.a1/..")).toEqual(true);
expect(isValidDirectoryPath("/dir_/._foo/.")).toEqual(true);
expect(isValidDirectoryPath("/./dir-/_foo")).toEqual(true);
expect(isValidDirectoryPath("/../dir-/_foo")).toEqual(true);
});
});
describe("isValidFilePath()", function() {
const isValidFilePath = dirHelpers.isValidFilePath;
it("should return false for strings that are too short", function() {
expect(isValidFilePath("/a")).toEqual(false);
expect(isValidFilePath("a.")).toEqual(false);
expect(isValidFilePath(".a")).toEqual(false);
expect(isValidFilePath("/.")).toEqual(false);
});
it("should return true for arguments that are just filenames", function() {
expect(isValidFilePath("test.txt")).toEqual(true);
expect(isValidFilePath("123.script")).toEqual(true);
expect(isValidFilePath("foo123.b")).toEqual(true);
expect(isValidFilePath("my_script.script")).toEqual(true);
expect(isValidFilePath("my-script.script")).toEqual(true);
expect(isValidFilePath("_foo.lit")).toEqual(true);
expect(isValidFilePath("mult.periods.script")).toEqual(true);
expect(isValidFilePath("mult.per-iods.again.script")).toEqual(true);
});
it("should return true for valid filepaths", function() {
expect(isValidFilePath("/foo/test.txt")).toEqual(true);
expect(isValidFilePath("/../123.script")).toEqual(true);
expect(isValidFilePath("/./foo123.b")).toEqual(true);
expect(isValidFilePath("/dir/my_script.script")).toEqual(true);
expect(isValidFilePath("/dir1/dir2/dir3/my-script.script")).toEqual(true);
expect(isValidFilePath("/dir1/dir2/././../_foo.lit")).toEqual(true);
expect(isValidFilePath("/.dir1/./../.dir2/mult.periods.script")).toEqual(true);
expect(isValidFilePath("/_dir/../dir2/mult.per-iods.again.script")).toEqual(true);
});
it("should return false for strings that end with a slash", function() {
expect(isValidFilePath("/foo/")).toEqual(false);
expect(isValidFilePath("foo.txt/")).toEqual(false);
expect(isValidFilePath("/")).toEqual(false);
expect(isValidFilePath("/_dir/")).toEqual(false);
});
it("should return false for invalid arguments", function() {
expect(isValidFilePath(null)).toEqual(false);
expect(isValidFilePath()).toEqual(false);
expect(isValidFilePath(5)).toEqual(false);
expect(isValidFilePath({})).toEqual(false);
})
});
describe("getFirstParentDirectory()", function() {
const getFirstParentDirectory = dirHelpers.getFirstParentDirectory;
it("should return the first parent directory in a filepath", function() {
expect(getFirstParentDirectory("/dir1/foo.txt")).toEqual("dir1/");
expect(getFirstParentDirectory("/dir1/dir2/dir3/dir4/foo.txt")).toEqual("dir1/");
expect(getFirstParentDirectory("/_dir1/dir2/foo.js")).toEqual("_dir1/");
});
it("should return '/' if there is no first parent directory", function() {
expect(getFirstParentDirectory("")).toEqual("/");
expect(getFirstParentDirectory(" ")).toEqual("/");
expect(getFirstParentDirectory("/")).toEqual("/");
expect(getFirstParentDirectory("//")).toEqual("/");
expect(getFirstParentDirectory("foo.script")).toEqual("/");
expect(getFirstParentDirectory("/foo.txt")).toEqual("/");
});
});
describe("getAllParentDirectories()", function() {
const getAllParentDirectories = dirHelpers.getAllParentDirectories;
it("should return all parent directories in a filepath", function() {
expect(getAllParentDirectories("/")).toEqual("/");
expect(getAllParentDirectories("/home/var/foo.txt")).toEqual("/home/var/");
expect(getAllParentDirectories("/home/var/")).toEqual("/home/var/");
expect(getAllParentDirectories("/home/var/test/")).toEqual("/home/var/test/");
});
it("should return an empty string if there are no parent directories", function() {
expect(getAllParentDirectories("foo.txt")).toEqual("");
});
});
describe("isInRootDirectory()", function() {
const isInRootDirectory = dirHelpers.isInRootDirectory;
it("should return true for filepaths that refer to a file in the root directory", function() {
expect(isInRootDirectory("a.b")).toEqual(true);
expect(isInRootDirectory("foo.txt")).toEqual(true);
expect(isInRootDirectory("/foo.txt")).toEqual(true);
});
it("should return false for filepaths that refer to a file that's NOT in the root directory", function() {
expect(isInRootDirectory("/dir/foo.txt")).toEqual(false);
expect(isInRootDirectory("dir/foo.txt")).toEqual(false);
expect(isInRootDirectory("/./foo.js")).toEqual(false);
expect(isInRootDirectory("../foo.js")).toEqual(false);
expect(isInRootDirectory("/dir1/dir2/dir3/foo.txt")).toEqual(false);
});
it("should return false for invalid inputs (inputs that aren't filepaths)", function() {
expect(isInRootDirectory(null)).toEqual(false);
expect(isInRootDirectory(undefined)).toEqual(false);
expect(isInRootDirectory("")).toEqual(false);
expect(isInRootDirectory(" ")).toEqual(false);
expect(isInRootDirectory("a")).toEqual(false);
expect(isInRootDirectory("/dir")).toEqual(false);
expect(isInRootDirectory("/dir/")).toEqual(false);
expect(isInRootDirectory("/dir/foo")).toEqual(false);
});
});
describe("evaluateDirectoryPath()", function() {
//const evaluateDirectoryPath = dirHelpers.evaluateDirectoryPath;
// TODO
});
describe("evaluateFilePath()", function() {
//const evaluateFilePath = dirHelpers.evaluateFilePath;
// TODO
})
});

View File

@@ -1,299 +0,0 @@
import * as dirHelpers from "../../src/Terminal/DirectoryHelpers";
import { expect } from "chai";
console.log("Beginning Terminal Directory Tests");
describe("Terminal Directory Tests", function() {
describe("removeLeadingSlash()", function() {
const removeLeadingSlash = dirHelpers.removeLeadingSlash;
it("should remove first slash in a string", function() {
expect(removeLeadingSlash("/")).to.equal("");
expect(removeLeadingSlash("/foo.txt")).to.equal("foo.txt");
expect(removeLeadingSlash("/foo/file.txt")).to.equal("foo/file.txt");
});
it("should only remove one slash", function() {
expect(removeLeadingSlash("///")).to.equal("//");
expect(removeLeadingSlash("//foo")).to.equal("/foo");
});
it("should do nothing for a string that doesn't start with a slash", function() {
expect(removeLeadingSlash("foo.txt")).to.equal("foo.txt");
expect(removeLeadingSlash("foo/test.txt")).to.equal("foo/test.txt");
});
it("should not fail on an empty string", function() {
expect(removeLeadingSlash.bind(null, "")).to.not.throw();
expect(removeLeadingSlash("")).to.equal("");
});
});
describe("removeTrailingSlash()", function() {
const removeTrailingSlash = dirHelpers.removeTrailingSlash;
it("should remove last slash in a string", function() {
expect(removeTrailingSlash("/")).to.equal("");
expect(removeTrailingSlash("foo.txt/")).to.equal("foo.txt");
expect(removeTrailingSlash("foo/file.txt/")).to.equal("foo/file.txt");
});
it("should only remove one slash", function() {
expect(removeTrailingSlash("///")).to.equal("//");
expect(removeTrailingSlash("foo//")).to.equal("foo/");
});
it("should do nothing for a string that doesn't end with a slash", function() {
expect(removeTrailingSlash("foo.txt")).to.equal("foo.txt");
expect(removeTrailingSlash("foo/test.txt")).to.equal("foo/test.txt");
});
it("should not fail on an empty string", function() {
expect(removeTrailingSlash.bind(null, "")).to.not.throw();
expect(removeTrailingSlash("")).to.equal("");
});
});
describe("isValidFilename()", function() {
const isValidFilename = dirHelpers.isValidFilename;
it("should return true for valid filenames", function() {
expect(isValidFilename("test.txt")).to.equal(true);
expect(isValidFilename("123.script")).to.equal(true);
expect(isValidFilename("foo123.b")).to.equal(true);
expect(isValidFilename("my_script.script")).to.equal(true);
expect(isValidFilename("my-script.script")).to.equal(true);
expect(isValidFilename("_foo.lit")).to.equal(true);
expect(isValidFilename("mult.periods.script")).to.equal(true);
expect(isValidFilename("mult.per-iods.again.script")).to.equal(true);
expect(isValidFilename("BruteSSH.exe-50%-INC")).to.equal(true);
expect(isValidFilename("DeepscanV1.exe-1.01%-INC")).to.equal(true);
expect(isValidFilename("DeepscanV2.exe-1.00%-INC")).to.equal(true);
expect(isValidFilename("AutoLink.exe-1.%-INC")).to.equal(true);
});
it("should return false for invalid filenames", function() {
expect(isValidFilename("foo")).to.equal(false);
expect(isValidFilename("my script.script")).to.equal(false);
expect(isValidFilename("a^.txt")).to.equal(false);
expect(isValidFilename("b#.lit")).to.equal(false);
expect(isValidFilename("lib().js")).to.equal(false);
expect(isValidFilename("foo.script_")).to.equal(false);
expect(isValidFilename("foo._script")).to.equal(false);
expect(isValidFilename("foo.hyphened-ext")).to.equal(false);
expect(isValidFilename("")).to.equal(false);
expect(isValidFilename("AutoLink-1.%-INC.exe")).to.equal(false);
expect(isValidFilename("AutoLink.exe-1.%-INC.exe")).to.equal(false);
expect(isValidFilename("foo%.exe")).to.equal(false);
expect(isValidFilename("-1.00%-INC")).to.equal(false);
});
});
describe("isValidDirectoryName()", function() {
const isValidDirectoryName = dirHelpers.isValidDirectoryName;
it("should return true for valid directory names", function() {
expect(isValidDirectoryName("a")).to.equal(true);
expect(isValidDirectoryName("foo")).to.equal(true);
expect(isValidDirectoryName("foo-dir")).to.equal(true);
expect(isValidDirectoryName("foo_dir")).to.equal(true);
expect(isValidDirectoryName(".a")).to.equal(true);
expect(isValidDirectoryName("1")).to.equal(true);
expect(isValidDirectoryName("a1")).to.equal(true);
expect(isValidDirectoryName(".a1")).to.equal(true);
expect(isValidDirectoryName("._foo")).to.equal(true);
expect(isValidDirectoryName("_foo")).to.equal(true);
});
it("should return false for invalid directory names", function() {
expect(isValidDirectoryName("")).to.equal(false);
expect(isValidDirectoryName("foo.dir")).to.equal(false);
expect(isValidDirectoryName("1.")).to.equal(false);
expect(isValidDirectoryName("foo.")).to.equal(false);
expect(isValidDirectoryName("dir#")).to.equal(false);
expect(isValidDirectoryName("dir!")).to.equal(false);
expect(isValidDirectoryName("dir*")).to.equal(false);
expect(isValidDirectoryName(".")).to.equal(false);
});
});
describe("isValidDirectoryPath()", function() {
const isValidDirectoryPath = dirHelpers.isValidDirectoryPath;
it("should return false for empty strings", function() {
expect(isValidDirectoryPath("")).to.equal(false);
});
it("should return true only for the forward slash if the string has length 1", function() {
expect(isValidDirectoryPath("/")).to.equal(true);
expect(isValidDirectoryPath(" ")).to.equal(false);
expect(isValidDirectoryPath(".")).to.equal(false);
expect(isValidDirectoryPath("a")).to.equal(false);
});
it("should return true for valid directory paths", function() {
expect(isValidDirectoryPath("/a")).to.equal(true);
expect(isValidDirectoryPath("/dir/a")).to.equal(true);
expect(isValidDirectoryPath("/dir/foo")).to.equal(true);
expect(isValidDirectoryPath("/.dir/foo-dir")).to.equal(true);
expect(isValidDirectoryPath("/.dir/foo_dir")).to.equal(true);
expect(isValidDirectoryPath("/.dir/.a")).to.equal(true);
expect(isValidDirectoryPath("/dir1/1")).to.equal(true);
expect(isValidDirectoryPath("/dir1/a1")).to.equal(true);
expect(isValidDirectoryPath("/dir1/.a1")).to.equal(true);
expect(isValidDirectoryPath("/dir_/._foo")).to.equal(true);
expect(isValidDirectoryPath("/dir-/_foo")).to.equal(true);
});
it("should return false if the path does not have a leading slash", function() {
expect(isValidDirectoryPath("a")).to.equal(false);
expect(isValidDirectoryPath("dir/a")).to.equal(false);
expect(isValidDirectoryPath("dir/foo")).to.equal(false);
expect(isValidDirectoryPath(".dir/foo-dir")).to.equal(false);
expect(isValidDirectoryPath(".dir/foo_dir")).to.equal(false);
expect(isValidDirectoryPath(".dir/.a")).to.equal(false);
expect(isValidDirectoryPath("dir1/1")).to.equal(false);
expect(isValidDirectoryPath("dir1/a1")).to.equal(false);
expect(isValidDirectoryPath("dir1/.a1")).to.equal(false);
expect(isValidDirectoryPath("dir_/._foo")).to.equal(false);
expect(isValidDirectoryPath("dir-/_foo")).to.equal(false);
});
it("should accept dot notation", function() {
expect(isValidDirectoryPath("/dir/./a")).to.equal(true);
expect(isValidDirectoryPath("/dir/../foo")).to.equal(true);
expect(isValidDirectoryPath("/.dir/./foo-dir")).to.equal(true);
expect(isValidDirectoryPath("/.dir/../foo_dir")).to.equal(true);
expect(isValidDirectoryPath("/.dir/./.a")).to.equal(true);
expect(isValidDirectoryPath("/dir1/1/.")).to.equal(true);
expect(isValidDirectoryPath("/dir1/a1/..")).to.equal(true);
expect(isValidDirectoryPath("/dir1/.a1/..")).to.equal(true);
expect(isValidDirectoryPath("/dir_/._foo/.")).to.equal(true);
expect(isValidDirectoryPath("/./dir-/_foo")).to.equal(true);
expect(isValidDirectoryPath("/../dir-/_foo")).to.equal(true);
});
});
describe("isValidFilePath()", function() {
const isValidFilePath = dirHelpers.isValidFilePath;
it("should return false for strings that are too short", function() {
expect(isValidFilePath("/a")).to.equal(false);
expect(isValidFilePath("a.")).to.equal(false);
expect(isValidFilePath(".a")).to.equal(false);
expect(isValidFilePath("/.")).to.equal(false);
});
it("should return true for arguments that are just filenames", function() {
expect(isValidFilePath("test.txt")).to.equal(true);
expect(isValidFilePath("123.script")).to.equal(true);
expect(isValidFilePath("foo123.b")).to.equal(true);
expect(isValidFilePath("my_script.script")).to.equal(true);
expect(isValidFilePath("my-script.script")).to.equal(true);
expect(isValidFilePath("_foo.lit")).to.equal(true);
expect(isValidFilePath("mult.periods.script")).to.equal(true);
expect(isValidFilePath("mult.per-iods.again.script")).to.equal(true);
});
it("should return true for valid filepaths", function() {
expect(isValidFilePath("/foo/test.txt")).to.equal(true);
expect(isValidFilePath("/../123.script")).to.equal(true);
expect(isValidFilePath("/./foo123.b")).to.equal(true);
expect(isValidFilePath("/dir/my_script.script")).to.equal(true);
expect(isValidFilePath("/dir1/dir2/dir3/my-script.script")).to.equal(true);
expect(isValidFilePath("/dir1/dir2/././../_foo.lit")).to.equal(true);
expect(isValidFilePath("/.dir1/./../.dir2/mult.periods.script")).to.equal(true);
expect(isValidFilePath("/_dir/../dir2/mult.per-iods.again.script")).to.equal(true);
});
it("should return false for strings that end with a slash", function() {
expect(isValidFilePath("/foo/")).to.equal(false);
expect(isValidFilePath("foo.txt/")).to.equal(false);
expect(isValidFilePath("/")).to.equal(false);
expect(isValidFilePath("/_dir/")).to.equal(false);
});
it("should return false for invalid arguments", function() {
expect(isValidFilePath(null)).to.equal(false);
expect(isValidFilePath()).to.equal(false);
expect(isValidFilePath(5)).to.equal(false);
expect(isValidFilePath({})).to.equal(false);
})
});
describe("getFirstParentDirectory()", function() {
const getFirstParentDirectory = dirHelpers.getFirstParentDirectory;
it("should return the first parent directory in a filepath", function() {
expect(getFirstParentDirectory("/dir1/foo.txt")).to.equal("dir1/");
expect(getFirstParentDirectory("/dir1/dir2/dir3/dir4/foo.txt")).to.equal("dir1/");
expect(getFirstParentDirectory("/_dir1/dir2/foo.js")).to.equal("_dir1/");
});
it("should return '/' if there is no first parent directory", function() {
expect(getFirstParentDirectory("")).to.equal("/");
expect(getFirstParentDirectory(" ")).to.equal("/");
expect(getFirstParentDirectory("/")).to.equal("/");
expect(getFirstParentDirectory("//")).to.equal("/");
expect(getFirstParentDirectory("foo.script")).to.equal("/");
expect(getFirstParentDirectory("/foo.txt")).to.equal("/");
});
});
describe("getAllParentDirectories()", function() {
const getAllParentDirectories = dirHelpers.getAllParentDirectories;
it("should return all parent directories in a filepath", function() {
expect(getAllParentDirectories("/")).to.equal("/");
expect(getAllParentDirectories("/home/var/foo.txt")).to.equal("/home/var/");
expect(getAllParentDirectories("/home/var/")).to.equal("/home/var/");
expect(getAllParentDirectories("/home/var/test/")).to.equal("/home/var/test/");
});
it("should return an empty string if there are no parent directories", function() {
expect(getAllParentDirectories("foo.txt")).to.equal("");
});
});
describe("isInRootDirectory()", function() {
const isInRootDirectory = dirHelpers.isInRootDirectory;
it("should return true for filepaths that refer to a file in the root directory", function() {
expect(isInRootDirectory("a.b")).to.equal(true);
expect(isInRootDirectory("foo.txt")).to.equal(true);
expect(isInRootDirectory("/foo.txt")).to.equal(true);
});
it("should return false for filepaths that refer to a file that's NOT in the root directory", function() {
expect(isInRootDirectory("/dir/foo.txt")).to.equal(false);
expect(isInRootDirectory("dir/foo.txt")).to.equal(false);
expect(isInRootDirectory("/./foo.js")).to.equal(false);
expect(isInRootDirectory("../foo.js")).to.equal(false);
expect(isInRootDirectory("/dir1/dir2/dir3/foo.txt")).to.equal(false);
});
it("should return false for invalid inputs (inputs that aren't filepaths)", function() {
expect(isInRootDirectory(null)).to.equal(false);
expect(isInRootDirectory(undefined)).to.equal(false);
expect(isInRootDirectory("")).to.equal(false);
expect(isInRootDirectory(" ")).to.equal(false);
expect(isInRootDirectory("a")).to.equal(false);
expect(isInRootDirectory("/dir")).to.equal(false);
expect(isInRootDirectory("/dir/")).to.equal(false);
expect(isInRootDirectory("/dir/foo")).to.equal(false);
});
});
describe("evaluateDirectoryPath()", function() {
//const evaluateDirectoryPath = dirHelpers.evaluateDirectoryPath;
// TODO
});
describe("evaluateFilePath()", function() {
//const evaluateFilePath = dirHelpers.evaluateFilePath;
// TODO
})
});

View File

@@ -1,24 +0,0 @@
<html>
<!-- NOT CURRENTLY USED. Used to run mocha in browser -->
<head>
<meta charset="utf-8">
<title>Mocha Tests</title>
<link href="https://unpkg.com/mocha@6.1.4/mocha.css" rel="stylesheet" />
</head>
<body>
<div id="mocha"></div>
<script defer src="https://unpkg.com/chai/chai.js"></script>
<script defer src="https://unpkg.com/mocha/mocha.js"></script>
<script type="module" class="mocha-init">
mocha.setup('bdd');
mocha.checkLeaks();
</script>
<script type="module" src="test.bundle.js"></script>
<script class="mocha-exec" type="module">
console.log("Running Tests");
mocha.run();
</script>
</body>
</html>

View File

@@ -1,5 +0,0 @@
export * from "./Netscript/DynamicRamCalculationTests";
export * from "./Netscript/StaticRamCalculationTests";
export * from "./StockMarketTests";
export * from "./StringHelperFunctionsTests";
export * from "./Terminal/DirectoryTests";

View File

@@ -48,6 +48,20 @@ function ScriptLogPopup(props: IProps): React.ReactElement {
removeElementById(props.id);
}
useEffect(() => {
function closeHandler(event: KeyboardEvent) {
if(event.keyCode === 27) {
close();
}
}
document.addEventListener('keydown', closeHandler);
return () => {
document.removeEventListener('keydown', closeHandler);
}
}, []);
function kill(): void {
killWorkerScript(props.script, props.script.server, true);
close();
@@ -57,7 +71,6 @@ function ScriptLogPopup(props: IProps): React.ReactElement {
event.preventDefault();
let x = event.clientX;
let y = event.clientY;
console.log(props.container.clientWidth);
let left = props.container.offsetLeft+props.container.clientWidth/2;
let top = props.container.offsetTop+props.container.clientWidth/5;
function mouseMove(event: MouseEvent): void {